TS STRUCTURE - Discriminated Union Types (标签联合类型 或 可辨识联合类型)

2023-05-17 19:42:36 浏览数 (2)

# Discriminated union type

Discriminated union type returns a new type that contains only the properties that are present in all interfaces.

代码语言:javascript复制
interface Square {
  kind: "square";
  size: number;
}
interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}
interface Circle {
  kind: "circle";
  radius: number;
}

function area (s: Square | Rectangle | Circle): number {
  switch (s.kind) {
    case "square":
      return s.size * s.size;
    case "rectangle":
      return s.height * s.width;
    case "circle":
      return Math.PI * s.radius ** 2;
  }
}

# Working with Interfaces

Note that our object has other properties, but the compiler only checks that it has at least the necessary properties, and their types match the required ones.

代码语言:javascript复制
function printLabel (labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = {
  size: 10,
  label: "Size 10 Object"
};
printLabel(myObj);

We can rewrite this example, this time using the interface to reflect the need for a string-type label property:

代码语言:javascript复制
interface LabelledValue {
  label: string;
}

function printLabel (labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {
  size: 10,
  label: "Size 10 Object"
};
printLabel(myObj);

If the object that is passed to the function meets the listed requirements, then it is considered suitable.

# Optional Properties

Not all interface properties may be required. Some exist only under certain conditions, or none at all.

代码语言:javascript复制
interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare (config: SquareConfig): { color: string; area: number } {
  let newSquare = { color: "white", area: 100 };
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({ color: "black" });

The advantage of optional properties is that you can describe the properties that may be present, and at the same time prohibit the use of those properties that are not part of the interface.

# Read-Only Properties

Some properties should only be modifiable when the object is created.

代码语言:javascript复制
interface Point {
  readonly x: number;
  readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // error!

# ReadonlyArray<T>

TS has a ReadonlyArray<T> type, which is essentially an Array<T> type, from which all methods that modify it are removed, so you can be sure that such arrays will notchange after creation:

代码语言:javascript复制
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;

ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error! even assigning ReadonlyArrayto a regular array is not allowed

// However, this restrictioncan still be circumvented by using typecasting
a = ro as number[];

# Checks for Extra Properties

Object literals are processed by it in a special way and are checked for the presence of unnecessary properties. This check is done when literals are either assigned to other variables or passed as arguments. If there are any properties in the literal that are not present in the target type, this will be considered an error.

代码语言:javascript复制
interface SquareConfig {
  color?: string;
  width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
  // ...
}
let mySquare = createSquare({ colour: "red", width: 100 }); // error, 'colour' not expected in type 'SquareConfig'

It is very easy to bypass such a check. The easiest way is to use typecasting:

代码语言:javascript复制
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

If you are sure that the object can have additional properties that will be used in some special way, then there is an even better way – to add a string index.

代码语言:javascript复制
interface SquareConfig {
  color?: string;
  width?: number;
  [propName: string]: any;
}

The last way to bypass the check for redundant properties – which may seem a little unexpected – is to assign an object to another variable. Since squareOptions will not pass the check for redundant properties, the compiler will not throw an error.

代码语言:javascript复制
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

# Functional Types

In addition to describing objects with properties, interfaces can also describe function types.

代码语言:javascript复制
interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;

// The parameter names do not have to match in order for the function to pass type-matching.
mySearch = function (src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
}

The function parameters are checked one after the other,and the parameter types that are in the corresponding positions are compared in pairs.

If you don’t want to specify types for arguments, then TS can infer types from the context based on the fact that the function is assigned to a variable whose type is SearchFunc.

代码语言:javascript复制
let mySearch: SearchFunc;
mySearch = function (src, sub) {
  let result = src.search(sub);
  return result > -1;
}

# Indexed Types

In the same way that interfaces are used to describe functions, you can describe types so that you can use the index operator with them.

Indexed types have an index signature that describes the types that can be used to index an object, as well as the types of values that this operation returns.

代码语言:javascript复制
interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: String = myArray[0];

There are only two types of supported index signatures: with strings and with numbers as an argument.

In addition to being a powerful way to describe dictionaries, string indexes require that the types of all properties match the type that the index operation returns. This is because obj.property is also available as obj[property].

0 人点赞