summary
Everything in Javascript is a variable, and so is Typescript. A type can also be used as a "variable" to perform some operations. For example, the advanced type introduced earlier is an extended type based on the basic type and composite type.
Some operators in Typescript can perform further operations on types, including:
- typeof: type acquisition
- keyof: index type acquisition
- as: type assertion
- []: member type
- Extensions: condition type
Operator
typeof: type acquisition
Javascript itself has a typeof operator, which is used to obtain the variable type name, and the return value is a string. Typeof in Typescript can be used for variables or types. When using variables, the effect is the same as that of Javascript. When using types, you can obtain the types of variables.
Example:
let a = { name: 'a', value: 0 } type A = typeof a let b: A = { name: 'b', value: 1 }
In the above example, A is the type obtained through typeof, which obtains the corresponding type through variables.
keyof: index type acquisition
The keyof operator accepts an object type and returns the literal type composed of its member names.
Example:
interface A { name: string value: number } type AMember = keyof A // 'name' | 'value'
In the above example, the type AMember is the literal type: 'name' | 'value'.
If the index member is declared in the object type, the keyof operation is the type of index name. Example:
interface A { [index: number]: string } type AMember = keyof A // number
When getting object members through index in Javascript, if the index name is a number, it will also be converted into a string. Therefore, in Typescript, if the index name is of string type, the result of keyof operation is' string '|' number '.
Applying: member name constraints
A common application is to use keyof to constrain object member names in generics.
Example:
function print<T, U extends keyof T>(target: T, name: U) { let value = target[name] console.log(value) }
In the above example, first obtain the possible value (literal type) of the member name of type T through keyof T, and then indicate that the type variable U must contain the types allowed by the members of type T through generic constraints (extensions). The effect is that the parameter name must be one of the member names of the parameter target.
as: type assertion
Type assertions are often misunderstood as type conversions, but assertions only tell the compiler that a variable is of a more specific or broader type and do not transform the variable into another type. This often happens when the compiler does not know the specific type of a variable, but only knows that it is a base class, while the user knows that the variable is a derived class, which can clearly tell the compiler its type.
The keyword as or angle brackets < > are used in Typescript to represent type assertions. Example:
let a = undefined as string | undefined a = 'a' let b = <{ value: number } | null>null b = { value: 0 }
'a' is an undefined variable and cannot be assigned to a string. We assert it as string | undefined, indicating that this type can be either string or undefined.
A common application is in browsers when we use document When getelementbyid method obtains the element object, the return value type of the method is HTMLElement, but we clearly know what type of element we obtain, so we can use type assertion to let the compiler know the specific type. Example:
let canvas = document.getElementById('canvas') as HTMLCanvasElement
[]: member type
Type can use the operator []. The operand is the member name and returns the type of the member.
Example:
interface A { name: string value: number } type AName = A['name'] // string
You can use literal union types to get a union type that consists of member types. Example:
type B = A['name' | 'value'] // string | number type C = A[keyof A] // string | number
If the type has an index, we can also get its index value type. Example:
interface A { [index: string]: boolean } type B = A[string] // boolean
Note that since the string type index name can be compatible with the number type, the type B of the above example can be changed to: type B = A[number], but for the number type index name, the string type cannot be used to obtain the index value type.
Extensions: condition type
Expression < type > extends < target >< True expression >: < false expression > you can determine the return type by judging whether the type meets the conditions.
Example:
interface A { name: string } interface B extends A { value: number } type C = B extends A ? string : number
Type C is determined by whether type B is compatible with type A. if compatible, it is string type, otherwise it is number type.
The meaning of compatibility here will be introduced later.
This feature is often used in generics. Example:
type C<T> = T extends A ? string : number type D = C<B> // string
infer
In order to enhance the ability of inferring types, Typescript adds the operator infer. Its function is "inferring". There is a type here. It can only be used in truth expression of condition type.
Example:
type Flatten<T> = T extends Array<infer Item> ? Item : Type
The function of this generic type is to return the element type if the T type is an array or a derived class of an array, otherwise return the type itself to achieve the purpose of "type flattening". Here, the function of infer Item is equivalent to "creating a type variable". It is speculated that there is a type for the following truth value expression.
Type diffusion
If the union type is passed into the generic type of conditional type expression, the types in the union type will be split and then combined.
Example:
type ToArray<T> = T extends any ? T[] : never type StrArrOrNumArr = ToArray<string | number> // string[] | number[]
It is equivalent to that string and number types are first split from string | number and enter toArray < T > to become string [] and number [], and then combined into string[] | number [].
If you want to avoid this behavior, you can write this:
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never type StrOrNumArr = ToArrayNonDist<string | number> // (string | number)[]