当前位置:网站首页>Typescript learning [9] generic
Typescript learning [9] generic
2022-06-09 04:46:00 【Large American without sugar】
What is generics ?
Java The interpretation of generics in language is : Generics are Type parameterization , That is to say, a specific type of A parameterized . Just like defining function parameters , We can define several type parameters for generics , And pass explicit type parameters to the generic type when calling . Generics are designed to Valid constraints on the relationship between type members , For example, function parameters and return values 、 The relationship between class or interface members and methods .
Generic Type Parameter
The most common scenario for generics is to Constrains the type of function arguments , We can define several parameters for a function that are explicitly typed only when called .
For example, one of the following definitions reflect function , It can accept a parameter of any type , And return the value and type of the parameter intact , So how do we describe this function ? It seems that it needs to be used unknown 了
function reflect(param: unknown) {
return param;
}
const str = reflect('string'); // str The type is unknown
const num = reflect(1); // num type unknown
here ,reflect Although the function can accept an arbitrary type of parameter and return the value of the parameter intact , However, the return value type does not meet our expectations . Because we want the return value type to correspond to the input parameter type one by one ( such as number Yes number、string Yes string), Not regardless of the type of input parameter , The return value is always unknown.
here , Generics can meet this demand , How to define a generic parameter ? First , We put the parameters param The type of is defined as a ( Type level ) Parameters 、 Variable , Not an explicit type , Wait until the function call to pass in an explicit type .
For example, we can use angle brackets <> The syntax defines a generic parameter for a function P, And designate param The type of parameter is P , As shown in the following code :
function reflect<P>(param: P) {
return param;
}
Here we can see , In angle brackets P Represents the definition of generic parameters ,param After P Indicates that the type of the parameter is generic P( That is, the type is affected by P constraint ).
We can also use generics to explicitly annotate the type of the return value , Although there is no need to ( Because the type of the return value can be inferred based on the context ). For example, call the following reflect when , We can use angle brackets <> Syntax to generic parameters P Explicitly pass in an explicit type .
function reflect<P>(param: P): P {
return param;
}
Then when you call a function , We also pass <> The syntax specifies the following string、number Type entry , Accordingly ,reflectStr The type is string,reflectNum The type is number.
const str = reflect<string>('string'); // str The type is string
const num = reflect<number>(7); // num The type is number
Generics can not only constrain the type of the entire parameter of a function , You can also constrain parameter properties 、 Types of members , For example, the type of the parameter can be an array 、 object , The following example :
function reflectArray<P>(param: P[]) {
return param;
}
const reflectArr = reflectArray([1, '1']); // reflectArr The type is (string | number)[]
Here we constrain param The type of is array , The element type of an array is a generic input parameter .
Be careful : Generic arguments to a function must be in conjunction with the arguments / Only when parameter members establish effective constraint relations can they have practical significance . For example, in the following example , We define a generic type that only constrains the return value type , It doesn't make any sense .
function uselessGeneries<P>(): P {
return void 0 as unknown as P;
}
We can define any number of generic arguments to a function , As shown in the following code :
function reflectExtraParams<P, Q>(p1: P, p2: Q): [P, Q] {
return [p1, [p2];
}
In the above code , We define a with two generic arguments (P and Q) Function of reflectExtraParams, And pass P and Q Constraint function parameters p1、p2 And the type of the return value .
Universal class
In the definition of the class , We can also use generics to constrain constructors 、 attribute 、 Type of method , As shown in the following code :
class Memory<S> {
store: S;
constructor(store: S) {
this.store = store;
}
set(store: S) {
this.store = store;
}
get() {
return this.store;
}
}
const numMemory = new Memory(1);
const getNumMemory = numMemory.get(); // The type is number
numMemory.set(2);// Write only number type
console.log(numMemory.get()); // 2
const strMemory = new Memory('');
const getStrMemory = strMemory.get(); // The type is string
strMemory.set('string'); // Write only string type
console.log(strMemory.get()); // string
First , We define a register class that supports reading and writing Memory, And use generic constraints Memory Class constructor function 、set and get Type of method parameter , Finally, the generic input parameters are instantiated number and string Two registers of type .
Generic classes are similar to generic functions in that , When creating a class instance , If a generic constrained parameter passes in an explicit value , Then the generic input parameter ( To be exact, the incoming type ) It can be defaulted
The generic type
stay TypeScript in , A type itself can be defined as a generic type with ambiguous type parameters , And can receive explicit types as input parameters , Thus deriving more specific types , As shown in the following code :
function reflect<P>(param: P):P {
return param;
}
const reflectFn: <P>(param: P) => P = reflect; // That's all right.
Here we are variables reflectFn Explicitly added generic type annotations , And will reflect Function is assigned to it as a value .
We can also put reflectFn The type annotation of is extracted as a type alias or interface that can be reused , As shown in the following code :
function reflect<P>(param: P):P {
return param;
}
type ReflectFunction = <P>(param: P) => P;
interface IReflcetFunction {
<P>(param: P): P;
}
const reflectFun2: ReflectFunction = reflect;
const reflectFun3: IReflcetFunction = reflect;
Move the definition of the type input parameter after the type alias or interface name , At this point, the defined type that receives a specific type input parameter and returns a new type is a generic type .
In the following example , We have defined two parameters that can be received P Generic types of
function reflect<P>(param: P):P {
return param;
}
type GenericReflectFunction<P> = (param: P) => P;
interface IGenericReflectFunction<P> {
(param: P): P;
}
const reflectFun4: GenericReflectFunction<string> = reflect; // Representational generics
const reflectFun5: IGenericReflectFunction<number> = reflect; // Representational generics
const reflectFn4Return = reflectFun4('string'); // Both input and return values must be string type
const reflectFn5Return = reflectFun5(1); // Both input and return values must be number type
In a generic definition , We can even use some type operators to express , So that generics can derive different types according to the types of input parameters , As shown in the following code :
type StringOrNumberArray<E> = E extends string | number ? E[] : E;
type StringArray = StringOrNumberArray<string>; // The type is string[]
type NumberArray = StringOrNumberArray<number>; // The type is number[]
type NeverGot = StringOrNumberArray<boolean>; // The type is boolean
Here we define a generic type , If the reference is number | string An array type is generated , Otherwise, the input parameter type is generated . and , We've also used and JavaScript Ternary expressions use the same syntax to express the logical relationship of type operations .
If we pass in a for the above generic string | boolean Union type as input parameter , What type will you get ?
type StringOrNumberArray<E> = E extends string | number ? E[] : E;
type BooleanOrString = string | boolean;
type WhatIsThis = StringOrNumberArray<BooleanOrString>; // It seems to be string | boolean ?
type BooleanOrStringGot = BooleanOrString extends string | number ? BooleanOrString[] : BooleanOrString; // string | boolean
You will find that the type of display will be boolean | string[] .
BooleanOrStringGot and WhatIsThis The types of these two type aliases are different ? This is what we call Assignment condition type .
** About the concept of assignment condition type , Official interpretation :** In case of condition type judgment ( For example, in the above example extends), If the input parameter is a union type , Will be disassembled into independent ( atom ) type ( member ) Perform type operations .
For example, in the above example string | boolean Enter the reference , First it is disassembled into string and boolean These two independent types , Then judge whether it is string | number A subset of types . because string Is a subset and boolean No , So what we finally got WhatIsThis The type is boolean | string[].
Be careful : Enum types do not support generics .
Generic constraint
As mentioned earlier, generics are like functions of types , It can abstract 、 Encapsulate and receive ( type ) Enter the reference , The generic input parameter also has the characteristics similar to the function input parameter . therefore , We can limit generic input parameters to a relatively more explicit set , To constrain the input parameters .
For example, as mentioned earlier, return parameters intact reflect function , We want to limit the types of the received parameters to a set of several primitive types , You can use “ Generic input parameter name extends type ” Grammar achieves this , As shown in the following code :
function reflectSpecified<P extends number | string | boolean>(param: P): P {
return param;
}
reflectSpecified('string');
reflectSpecified(7);
reflectSpecified(true);
reflectSpecified(null); // ts(2345) 'null' Cannot assign type 'number | string | boolean'
Again , We can also constrain the interface generic input parameters to a specific scope , As shown in the following code :
interface ReduxModelSpecified<State extends {
id: number; name: string }> {
state: State;
}
type ComputedReduxModel1 = ReduxModelSpecified<{
id: number; name: string }>;
type ComputedReduxModel2 = ReduxModelSpecified<{
id: number; name: string; age: number }>;
type ComputedReduxModel3 = ReduxModelSpecified<{
id: string; name: number }>; // ts(2344)
type ComputedReduxModel4 = ReduxModelSpecified<{
id: number }>; // ts(2344)
In the example above ,ReduxModelSpecified Generics only receive { id: number; name: string } Subtypes of interface types are used as input parameters .
We can also set constraints between different generic input parameters , As shown in the following code :
interface ObjSetter {
<O extends {
}, K extends keyof O, V extends O[K]>(obj: O, key: K, value: V): V;
}
const setValueOfObj: ObjSetter = (obj, key, value) => (obj[key] = value);
setValueOfObj({
id: 1, name: 'name' }, 'id', 2);
setValueOfObj({
id: 1, name: 'name' }, 'name', 'newName');
setValueOfObj({
id: 1, name: 'name' }, 'age', 2);
setValueOfObj({
id: 1, name: 'name' }, 'id', '2');
When setting the function type of the object property value , It has a 3 Generic arguments : The first 1 One is the object , The first 2 One is the 1 A subset of the set of input parameter attribute names , The first 3 Is a subtype of the specified attribute type .
in addition , There is another similarity between generic input and function input , It can also specify default values for generic input parameters ( Default type ), And the syntax is completely consistent with the default parameters of the specified function , As shown in the following code :
interface ReduxModelSpecified<State extends {
id: number; name: string }> {
state: State
}
interface ReduxModelSpecified2<State = {
id: number; name: string }> {
state: State
}
type ComputedReduxModel5 = ReduxModelSpecified2;
type ComputedReduxModel6 = ReduxModelSpecified2<{
id: number; name: string; }>;
type ComputedReduxModel7 = ReduxModelSpecified; // ts(2314) Missing a type parameter
In the example above , We have defined generic types with default types as input parameters ReduxModelSpecified2, Therefore use ReduxModelSpecified2 Time type input parameters can be defaulted . and ReduxModelSpecified There is no default value for the input parameter of , So the default input parameter will prompt a type error .
The constraints and default values of generic input parameters can also be combined , As shown in the following code :
interface ReduxModelMixed<State extends {
} = {
id: number; name: string }> {
state: State
}
Here we qualify generics ReduxModelMixed Enter the reference State Must be {} Subtypes of types , It also specifies that the default type of the input parameter is the interface type by default { id: number; name: string; }.
边栏推荐
- 150 basic acoustic knowledge that acoustic engineers should know (full text)
- big.js、bignumber.js 和 decimal.js 的区别
- How to set up FAQ documents? Just four steps
- (4)数据响应式
- Keepalived configure virtual IP
- (4) Data responsive
- Win10 installing appium environment
- 5. Quick (Group) sort
- Give an example to illustrate the cell and num of lstmcell in TF_ What does unit mean
- Multi table query in MySQL
猜你喜欢

(3) Data binding instructions

Openinfra Foundation launched the "targeted fund" program to promote successful open source governance experience

蘑菇街半年营收1.68亿:同比降29% 运营亏损2.4亿

Proteus simulation Arduino

"Diwen Cup" skill competition between teachers and students of Electrical Engineering Department of Zibo technician college was successfully held

Devin round smart screen comes into the market

Question bank and answers of G3 boiler water treatment examination in 2022

2022 test question bank and simulation test of special operation certificate for installation, maintenance and demolition at heights

(7)属性绑定

Internet behavior networking
随机推荐
TypeScript学习【6】 接口类型
zsh
PHP e-signature SaaS API docking process
(6) Events
Proteus simulation Arduino
OpenGL 01 - créer une fenêtre
P5321 [bjoi2019] farewell (LCT)
Brain: an interpretable deep learning framework for Alzheimer's disease (AD) classification
(9)分支循环结构
[006] [ESP32开发笔记] 使用Flash下载工具烧录固件步骤
TypeScript学习【5】 类类型
proteus仿真Arduino
My creation anniversary
2022安全员-C证考试练习题模拟考试平台操作
(4) Data responsive
Template: constant coefficient homogeneous linear recurrence (linear algebra, polynomial)
TypeScript 学习【9】泛型
P1779 Xiaohu's springboard
(5) Bidirectional data binding
Nacos1.1.4版本本地源码启动