An overview of the
Typescript advanced types refer to types that operate on primitive and compound types, including:
- The joint
- cross
- The generic
- Literal type
Type the alias
Before getting into the specifics of advanced types, let’s take a look at Typescript type aliases. A type alias is also a type that uses a single word to represent a potentially complex type declaration, represented by the keyword type.
Example:
type S = string
let a: S = 'a'
Copy the code
Here we use S as the alias for string, using the same method as string.
An alias can represent any type, not just a basic type. Example:
type SA = string[] // represents an array of strings
type Handler = (x: string) = > void // represents the function
type I = {
// Represents the interface
name: string
value: number
}
class C {
name: string = 'a'
value: number = 0
}
type D = C / / on behalf of the class
Copy the code
type
The joint
Joint type refers to the variable for one of the multiple type and the relationship between “or”, represented by an operator |.
Example:
type StringOrNumber = string | number
let a: StringOrNumber = 'a'
let b: StringOrNumber = 0
function log(x: string | number) {
console.log(x)
}
Copy the code
StringOrNumber represents a type that can be either a string or a number, so variables of type StringOrNumber can be assigned as either a string or a number.
In Typescript, null and undefined are on the same level as other types. They are not subtypes of any type, which means we cannot use them to represent a null pointer to a particular type. But we do need to keep a variable null when it is indeterminate, and we can use the union type to indicate that a variable can be null or undefined. Example:
interface A {
name: string
}
let a: A | null = null
Copy the code
Ternary conditional operator
The return value type of a ternary conditional operator expression may be a union type.
Example:
let a = Math.random() < 0.5 ? 'a' : 0 // string | number
Copy the code
Because of the ternary conditional operator expression may return a string or number, so the return value is of type string | number
cross
A cross type is a combination of multiple types into a single type, and is represented by the & operator.
Example:
interface A {
x: string
y: number
}
interface B {
x: string
z: number
}
type C = A & B
let c: C = { x: 'x'.y: 0.z: 1 }
Copy the code
A member of an interleaved type contains all the members of the original type. For example, in the code above, A c variable must be of type A and type B, that is, it must have attributes required by both types.
There are some types that cannot be crossed, such as primitive types. Example:
type A = string & number
Copy the code
Because A variable cannot be both A string and A number, the ultimate A type is never.
If the two types being crossed have members of different types with the same name, they are also crossed. Example:
interface A {
x: { a: string; b: number}}interface B {
x: { a: string; c: number}}type C = A & B
let c: C = { x: { a: 'a'.b: 0.c: 1}}Copy the code
Types A and B have members x of the same name but of different types. Two X members can also be crossed when they are crossed, so the final type of x is {A: string, B: number, c: number}.
Literal type
Typescript literals can also be used as types. Such as:
let a: 'A'
Copy the code
The above declaration means that the value of variable a can only be ‘a’, and an error will be reported if it is assigned to any other value.
Note the difference with variable A here, namely:
const A = 'A'
type A = 'A'
Copy the code
Const A = ‘A’ indicates that the value of variable A is ‘A’, and type A = ‘A’ indicates A type that can only be assigned to A. They are used in different scenarios.
Numbers and booleans can also be literal types. This may not seem very useful, but this feature is typically used in conjunction with unions. Such as:
type Option = 'A' | 'B' | 'C' | 'D'
let a: Option = 'A'
let e: Option = 'E' / / error
Copy the code
A variable of type Option can only be assigned A value of ‘A’, ‘B’, ‘C’, or ‘D’. This is similar to enumeration, for example:
enum Option {
A,
B,
C,
D
}
Copy the code
The difference is that literal types are essentially strings, and all the methods of strings can be used. Also, with the keyof operator, we can dynamically generate literal types, as we’ll see later.
The generic
Generics in Typescript are similar to generics in other object-oriented languages, including: generic functions, generic classes, and generic interfaces.
Generic function
Example:
function merge<T.U> (x: T, y: U) :{ x: T; y: U } {
let t: { x: T; y: U } = { x, y }
return t
}
merge<string.number> ('a'.0)
Copy the code
We can declare the generic variable used in the function inside brackets, which represents the type passed in when the function is called. The type variable T can be used anywhere a type declaration is required, such as parameter types, return value types, local variable types, and so on.
Arrow functions can also contain generics. Example:
const merge =
(x: T, y: U): { x: T; y: U } => { let t: { x: T; y: U } = { x, y } return t }
,>Copy the code
A generic class
Example:
class Merge<T.U> {
x: T
y: U
constructor(x: T, y: U) {
this.x = x
this.y = y
}
merge(): { x: T; y: U } {
let t: { x: T; y: U } = { x: this.x, y: this.y }
return t
}
}
let merge = new Merge<string.number> ()Copy the code
We can declare the generic variable used in the class inside brackets, which represents the type passed in when the class is instantiated. The type variable T can be used anywhere a type declaration is required, such as members, constructor parameter types, and so on.
A generic interface
Example:
interface Merge<T, U> {
x: T
y: U
}
let merge: Merge<string.number> = { x: 'a'.y: 0 }
Copy the code
We can declare generic variables used in the interface inside brackets, which represent the type passed in when we declare the variable type. The type variable T can be used anywhere a type declaration is required, such as members, method parameter types, and so on.
Generic constraint
Because in a generic type, a generic variable is passed in when it is used, its specific type cannot be known at compile time, so reading any member of a variable of the class type does not work. Such as:
function scale<T> (x: T) :number {
return x.length / / error
}
Copy the code
While we may know in our minds that we pass in a variable with a length member (such as a string), the compiler doesn’t know that at compile time. Therefore, we need to tell the compiler what type T is that has a length member. Use the extends keyword:
interface HasLength {
length: number
}
function scale<T extends HasLength> (x: T) :number {
return x.length
}
scale('abc')
Copy the code
We use extends to constrain the type variable T, which must have a length attribute. That is, T must implement or inherit from HasLength.
The default type
Generic parameter lists can have default types declared in a similar way to default parameters.
Example:
function print<T.U = number> (x: T, y: U) {
// ...
}
print<string> ('x'.0)
Copy the code
Similarly, the type parameter following the default type parameter must also have a default type.
Preset generic
Some of the more advanced uses of Typescript are made possible by generics that are preset in the compiler’s native library, such as getting function return value types:
function scale(x: string) :number {
return x.length
}
let a: ReturnType<typeof scale> = scale('a')
Copy the code
In the above example, the typeof the scale function is obtained using the typeof operator, and the return value typeof the function type is obtained using the preset generic ReturnType.
More preset generics can be found in the Typescript documentation utility-types.