“Don’t be afraid, don’t settle, work hard in the future” — Hello! I am little sesame 😄
1. Cross types&
The & operator allows you to superimpose existing types into a single type that contains all of the required characteristics of the type.
- Cross typing is merging multiple types into one type. A cross type is simply a union of attributes of two interface types
type PersonName = { name: string }
type Person = PersonName & { age: number }
let person: Person = {
name: 'Little Golden Sesame'.age: 18
}
Copy the code
In the above code we define the PersonName type, then use the & operator to create a new Person type representing a type containing name and age, and then define and initialize a variable of type Person.
- In the process of merging multiple types, it happens that some types have the same members, but the corresponding types are inconsistent
interface X {
c: string;
d: string;
}
interface Y {
c: number;
e: string
}
type XY = X & Y;
type YX = Y & X;
let p: XY = { c: 6.d: "d".e: "e" }; // ERROR cannot assign type "number" to type "never".
let q: YX = { c: "c".d: "d".e: "e" }; // ERROR cannot assign type "string" to type "never".
Copy the code
In the code above, interfaces X and Y both have the same member C, but they are of different types. In this case, the type of member C of XY or YX is string & number, that is, the type of member C is both string and number. Obviously this type does not exist, so member C is of type never. [2]
2. Association type|
Â
Joint type using | separated for each type.
- Union Types indicate that the value can be one of many Types
- When unassigned, only properties and methods that are common to both types can be accessed on the union type
let name: string | number
name = 3
name = 'Little Golden Sesame'
Copy the code
Here let name: string | number means, allow the type of the name is a string or number, but not other types.
name = true / / the ERROR type "true" can not be assigned to a type of "string | number".
Copy the code
Access a property or method of the union type
When TypeScript doesn’t know what type a variable of a union type is, we can only access properties or methods that are common to all types of the union type:
function getLength(something: string | number) :number {
return something.length; // ERROR
}
Copy the code
In the case,length
notstring
 和 number
The common property of the.
There is no problem accessing the common attributes of string and number:
function getString(something: string | number) :string {
return something.toString();
}
Copy the code
When a variable of the union type is assigned, it can infer a type according to the rules of type inference:
let name: string | number
name = 3
console.log(name.length) // The attribute "length" does not exist on ERROR type "number".
name = 'Little Golden Sesame'
console.log(name.length) / / 5
Copy the code
In the example above, the name in the second line is inferred to be number, and an error is reported when accessing its length attribute.
The name of the fourth line is inferred to be a string, and accessing its length attribute is not an error.
3. Literal types
- You can combine strings, numbers, and Boolean literals into a union type
type ZType = 1 | 'one' | true
let t1: ZType = 1
let t2: ZType = 'one'
let t3: ZType = true
// The literal type
let Gender3: 'BOY' | 'GIRL'
Gender3 = 'BOY' // It can compile
Gender3 = 'GIRL' // It can compile
Gender3 = true // Failed to compile
Copy the code
3.1 String literal types
The string literal type is used to constrain the value to one of several strings.
type Direction = 'North' | 'East' | 'South' | 'West';
function move(distance: number, direction: Direction) {
// ...
}
move(1.'North'); // YES
move(1.'Little Sesame'); // ERROR, parameters of type "little sesame" cannot be assigned to parameters of type "Direction".
Copy the code
In the example above, we use type to specify a string literal type Direction, which can only take one of the four strings.
Note that both type aliases and string literal types are defined using type.
String literals VS union types
- The string literal type is used to restrict the value to a
Several strings
Where the union type indicates that the value can beA variety of types
One of the - String literals limit where they are used to accept only certain values. Union types are not limited to values, only the types of qualified values need to be consistent
3.2 Numeric literal types
Similar to string literals
type Direction = 11 | 12 | 13
function move(distance: number, direction: Direction) {
// ...
}
move(1.11); // YES
move(1.1); // ERROR, parameters of type 1 cannot be assigned to parameters of type Direction.
Copy the code
4. Index typekeyof
- Using index types, the compiler can examine code that uses dynamic attribute names.
As follows, take the values of some properties from the object to form a new array.
let obj = {
a: 1.b: 2.c: 3
}
function getValues(obj: any, keys: string[]) {
return keys.map(key= > obj[key])
}
console.log(getValues(obj, ['a'.'b'])) // [1, 2]
console.log(getValues(obj, ['a'.'f'])) // [ 1, undefined ]
Copy the code
In the above example, we want to throw an exception if the value of obj does not exist, so we want to throw an exception if the value of obj does not exist.
It can be accomplished mainly by the following three points:
- Query operators for index types:
keyof T
(Union type representing literals of all common properties of type T)- Index access operators:
T[K]
- Generic constraints:
T extends U
Let’s begin by modifying the getValues function based on the above conditions
- First we need a generic
T
It represents the parameter passed inobj
Because we cannot determine the parameters when we write the codeobj
What exactly is the type of, so get in this caseobj
Must use the future-oriented type – generics.
function getValues< T> (obj: T, keys: string[]) {
return keys.map(key= > obj[key])
}
Copy the code
- So the second argument passed in
keys
, which features an array whose members must be defined by parametersobj
Property name, at which point we can easily think of the operator we just learnedkeyof
.keyof T
On behalf of the parameterobj
Type of the joint type of the attribute name of our parameterkeys
Member type ofK
You just have to constrain tokeyof T
Can.
function getValues<T.K extends keyof T> (obj: T, keys: K[]) {
return keys.map(key= > obj[key])
}
Copy the code
- The return value is, we pass the type accessor
T[K]
I can get the type of the corresponding property values, their arrayT[K][]
Is the type of the return value.
function getValues<T.K extends keyof T> (obj: T, keys: K[]) :T[K] []{
return keys.map(key= > obj[key])
}
Copy the code
At this point our function is completely reinvented, so let’s try printing:
console.log(getValues(obj, ['a'.'b'])) // [1, 2]
console.log(getValues(obj, ['a'.'f'])) / / the ERROR is not able to type "" f" assigned to "type" a "|" "|" b "" c".
Copy the code
The getValues function in TypeScript, which uses index types with type operators, not only provides tighter type constraints, but also provides stronger code hints.
5. Mapping typein
A common task is to make each attribute of a known type optional:
interface Person {
name: string
age: number
gender: 'male' | 'female'
}
Copy the code
[K in Keys] [K in Keys] [K in Keys] [K in Keys] [K in Keys]
- K: Type variable, bound to each property in turn, corresponding to the type of each property name
- Keys: an associative type of string literals representing a set of attribute names
So how do we do that?
-
First, we need to find Keys, the associative type of string literals, using the keyof operator mentioned above. We pass in type Person and get keyof Person, the associative type of the attribute name of type Person.
-
Then we need to map the keyof Person attribute names one by one [key in keyof Person]. If we want to change all the attribute members to optional types, we need Person[key] to fetch the corresponding attribute values. Finally, we will regenerate an optional new type {[key in keyof Person]? : the Person [key]}.
Denoted by a type alias is:
type PartPerson = {
[key inkeyof Person]? : Person[key] }let p1: PartPerson = {}
// Generics can also be used
type Part<T> = {
[key inkeyof T]? : T[key] }let p2: Part<Person> = {}
Copy the code
Indeed, all attributes have become optional types
5.1 Mapping Types of Built-in Tools
- TS has some built-in utility types to help us use the type system better (see how this works in lib.es5.d.ts)
interface obj {
a: string;
b: number;
c: boolean;
}
Copy the code
5.1.1 Partial
- Partial
returns a Partial
that changes an attribute passed from non-optional to optional:
type PartialObj = Partial<obj>
Copy the code
5.1.2 Required
- Required
Returns the attribute passed as Required:
type RequiredObj = Required<PartialObj>
Copy the code
5.1.3 Readonly
- Readonly
Can make an incoming property read-only:
type ReadonlyObj = Readonly<obj>
Copy the code
5.1.4 ensuring a Pick
- Pick
,>
helps us return an item from an attribute passed in
type PickObj = Pick<obj, 'a' | 'b'>
// Retrieve a and b attributes from obj
Copy the code
interface Animal {
name: string
age: number
}
// Pick the name attribute from Animal
type AnimalSub = Pick<Animal, 'name'> // {name: string}
let a: AnimalSub = { name: 'Little Golden Sesame' }
Copy the code
5.1.5 Record
- Record<T, U> will string literal type
T
For all string variables of the new typeKey
Value,U
Type asKey
The type of
type RecordObj = Record<'x' | 'y', obj>
Copy the code
5.2 Control of mapping type modifiers
- TypeScript 2.8 adds control over mapping type modifiers
- To be specific, one
readonly
或?
Modifiers can be prefixed in a mapping type+
or-
To indicate that the modifier should be added or removed - Some of the built-in tool types in TS take advantage of this feature (Partial, Required, Readonly… Here we can refer to the Partial and Required implementations
type MutableRequired<T> = { -readonly [P inkeyof T]-? : T[P] };// Remove readonly and?
type ReadonlyPartial<T> = { +readonly [P inkeyof T]+? : T[P] };// Add readonly and?
Copy the code
A modifier without a + or – prefix has the same effect as a modifier with a + prefix. So the ReadonlyPartial
type above is the same as the one below
type ReadonlyPartial<T> = { readonly [P inkeyof T]? : T[P] };// Add readonly and?
Copy the code
6. Condition types
TypeScript 2.8 introduces conditional types, which can represent non-uniform types. A condition type uses a condition expression for type relationship detection to choose between two types:
T extends U ? X : Y
Copy the code
The above type means that the type is X if T can be assigned to U, and Y otherwise. Similar to the ternary conditional operator in JavaScript.
6.1 Distributed Condition Types
When the type examined within a Conditional type is naked Type parameter, it is called a distributed Conditional type. It is special in the joint type, it can automatically take A simple example, assuming that the T type is A | B | C, then it will be parsed into three conditions branch, as shown below.
A|B|C extends U ? X : Y
/ / equivalent to the
A extends U ? X : Y | B extends U ? X : Y | C extends U ? X : Y
Copy the code
No naked type parameter
If T or U contain type variables, parsing is delayed, that is, until the type variables all have concrete types before the result of the conditional type can be computed. In the following example, a Person interface is created, the return type of the declared global function add() changes depending on whether it is a subtype of Person, and the add() function is called in the generic function func().
interface Person { name: string; age: number; getName(): string; } declare function add\ <T> (x: T) :T extends Person ? string : number; function func\ <U> (x: U) { let a = add(x); let b: string | number = a; } Copy the code
Although the type of variable A is uncertain, the result of the condition type is either string or number, so it can be successfully assigned to variable B.
- Distributed conditional types can be used to Filter union types, as shown below. The Filter
,>
types remove subtypes of U from T.
type Filter<T, U> = T extends U ? never : T;
type T1 = Filter<"a" | "b" | "c" | "d"."a" | "c" | "f">; // "b" | "d"
type T2 = Filter<string | number | (() = > void), Function>; // string | number
Copy the code
- Distributed condition types can also be used together with mapping types for specific type mapping. That is, different source types correspond to different mapping rules, such as method names of mapping interfaces, as shown in the following figure.
interface Person {
name: string;
age: number;
getName(): string;
}
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];
type T3 = FunctionPropertyNames<Person>; // "getName"
Copy the code
6.2 Type Inferenceinfer
In the extends clause of conditional types, it is possible to introduce a type variable to be inferred through the Infer statement, and multiple Infer statements of the same type can occur, such as using the Infer statement to extract the return value type of a function, as shown below. It is important to note that type variables declared by Infer can only be used in the True branch.
type Func<T> = T extends(... args:any[]) => infer R ? R : any;
Copy the code
- When a function has an overload, the last function signature is inferred, as shown below, where ReturnType
is the built-in conditional type to get the return value type of function type T.
declare function load(x: string) :number;
declare function load(x: number) :string;
declare function load(x: string | number) :string | number;
type T4 = ReturnType<typeof load>; // string | number
Copy the code
- Note that the infer statement cannot be used in constraint substatements of normal type parameters, as shown below.
type Func<T extends(... args:any[]) => infer R> = R; // Error, not supported
Copy the code
- However, you can remove the type variable from the constraint and transfer it to the condition type to achieve the same effect, as shown below.
type AnyFunction = (. args:any[]) = > any;
type Func<T extends AnyFunction> = T extends(... args:any[]) => infer R ? R : any;
Copy the code
6.3 Predefined Condition types
TypeScript 2.8 adds predefined conditional types in lib.d.ts (see lib.es5.d.ts;). :
- Exclude
,>
: Exclude the subtype of U from T.
- 2) Extract
,>
: Extract subtypes of U from T
- 3) NonNullable
: Remove null and undefined from T.
- 4) ReturnType
: Get the return value type of the function.
- 5) InstanceType
: get the constructor InstanceType.
6.3.1 Exclude
- Exclude U from the types to which T can be assigned
type E = Exclude<string | number.string>
let e: E = 10 // number
Copy the code
6.3.2 Extract
- Extract U from the type T can be assigned to
type E = Extract<string | number.string>
let e: E = '1' // string
Copy the code
6.3.3 NonNullable
- Exclude null and undefined from T
type E = NonNullable<string | number | null | undefined>
let e: E = '1' // string | number
Copy the code
6.3.4 ReturnType
- Gets the return type of the function type
function getUserInfo() {
return { name: 'Little Golden Sesame'.age: 10}}type UserInfo = ReturnType<typeof getUserInfo>
let user: UserInfo = { name: 'Little Golden Sesame'.age: 10 } // {name: string; age: number; }
Copy the code
6.3.5 InstanceType
- Gets the instance type of the constructor
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
type P = InstanceType<typeof Person>
let p: P = new Person('1') // Person
Copy the code
reference
[1]. TypeScript Chinese
[2]. A rare TS Study Guide (1.8W word)
[3].typescript (6) — advanced types