Typescript泛型
软件工程的一个主要部分是构建组件,这些组件不仅具有定义明确且一致的 API,而且还可以重用。 能够处理今天和明天的数据的组件将为你提供构建大型软件系统的最灵活的能力。
使用泛型类型变量
function identity<Type>(arg: Type): Type {
return arg;
}
如果我们想调用arg的长度
当我们这样做时,编译器会给我们一个错误,我们正在使用arg的.length成员,但我们没有说arg有这个成员,这些类型变量代表任何和所有的类型,因此使用此函数的人可能会传入一个number代表,他没有.length成员。
function loggingIdentity<Type>(arg: Type[]): Type[] {
console.log(arg.length);
return arg;
}
//或者
function loggingIdentity<Type>(arg: Array<Type>): Array<Type> {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
keyof类型运算符
keyof运算符采用对象类型并生成其键的字符串或数字字面联合。以下类型p与type P =“x”| "y"类型相同
type Point = { x: number; y: number };
type P = keyof Point;
如果该类型具有 string 或 number 索引签名,则 keyof 将返回这些类型:
引用到索引类型上
type Arrayish={[n:number]:unknown}
type A = keyof Arrayish;
//type A = number
type Mapish = { [k: string]: boolean };
type M = keyof Mapish;
//type M = string | number
typeof 类型运算符返回类型
返回类型
- 使用ReturnType可以收到基本类型的返回类型
type Predicate = (x: unknown) => boolean;
type K = ReturnType<Predicate>;
// type K = boolean
- 如果在对象上使用需要使用typeof
更改为下
这里需要注意,Typescript有意限制你的可以使用typeof的表达式类型
具体来说,在标识符(即变量名)或其属性上使用typeof时唯一合法的。这有助于避免编写你认为正在执行但不是的代码混乱陷阱
索引访问类型
这里的索引访问类型和索引类型不同,索引类型是如[prop:number]:string等,索引访问类类型单纯是通过索引来访问类型
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];
//type Age = number
索引类型基本就是一种类型,所以我们可以完全使用联合、keyof或其他类型
type Person = { age: number; name: string; alive: boolean };
type I1 = Person["age" | "name"];
//type I1 = string | number
type I2 = Person[keyof Person];
//type I2 = string | number | boolean
type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];
//type I3 = string | boolean
//如果没有属性直接使用也会报错
type I1 = Person["alve"];
Property 'alve' does not exist on type 'Person'.
使用任意类型进行索引的另一个示例时使用number来获取数组元素的类型,我们可以将它与typeof结合起来。以方便的获取数组字面量的元素类型
以上获取数组中对象一行的数据类型也是我一直不知道怎么处理的,现在终于有了解决答案
在索引时使用类型,不能使用const否则不生效
用类型进行重构
type key = "age";
type Age = Person[key];
条件类型
有一个很好的案例记录保留一下
条件类型约束
通常,条件类型的检查会为我们提供一些新消息,就像使用类型保护进行缩小可以为我们提供更具体的类型一样,条件类型真正分支将通过我们检查的类型进一步限制泛型
type MessageOf<T> = T["message"];
Type '"message"' cannot be used to index type 'T'.
在这个例子中,Typescript出错是因为T不知道有一个名为message的属性,我们可以约束T,Typescript将不在报错
type MessageOf<T extends { message: unknown }> = T["message"];
interface Email {
message: string;
}
type EmailMessageContents = MessageOf<Email>;
//type EmailMessageContents = string 结果展示
在条件类型中推断
我们刚刚发现自己使用条件类型来应用约束,然后提取类型。这最终成为一种常见的操作,条件类型使它更容易。条件类型为我们提供了一种infer关键字从我们true分支中比较的类型进行推断方法。例如,我们可以推断出Flatten中的元素类型,而不是使用索引访问类型从manually中获取它:
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
我们使用infer关键字声明性的引入了一个名为Item的新泛型类型变量,而不是指定如何在true分支中检索Type的元素类型。这是我们不必考虑如何挖掘和探索我们感兴趣的类型的结构。
映射类型
当你不想重复自己时,有事一种类型需要基于另一种类型
映射类型建立在索引签名语法上用于声明未提前声明的属性类型、
interface Horse={};
type OnlyBoolsAndHorses = {
[key: string]: boolean | Horse;
};
const conforms: OnlyBoolsAndHorses = {
del: true,
rodney: false,
};
映射类型是一种泛型类型,它使用PropertyKey(经常通过keyof创建)的联合连遍历键以创建类型:
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
上面的代码会将Type类型的所有属性并将其值更改为布尔值
映射修饰符
在映射期间可以应用两个额外的修饰符:readonly和?分别影响可变性和可选性。
可以通过加前缀-或+来一处或添加这些修饰符。如果你不添加前缀,则假定+.
- 移出readonly
// Removes 'readonly' attributes from a type's properties
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
//得出的结果
type UnlockedAccount = {
id: string;
name: string;
}
- 移出非必选?
// Removes 'optional' attributes from a type's properties
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User = Concrete<MaybeUser>;
//得出的结果
type User = {
id: string;
name: string;
age: number;
}
- 通过as重新映射
type MappedTypeWithNewProperties<Type> = {
[Properties in keyof Type as NewKeyType]: Type[Properties]
}
可以利用模板字面类型重新创建属性名称
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
//结果
type LazyPerson = {
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
可以通过条件类型生成never来过滤掉键
// Remove the 'kind' property
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
//结果
type KindlessCircle = {
radius: number;
}
你可以映射任意联合,不仅是 string | number | symbol 的联合,还可以映射任何类型的联合:
type EventConfig<Events extends { kind: string }> = {
[E in Events as E["kind"]]: (event: E) => void;
}
type SquareEvent = { kind: "square", x: number, y: number };
type CircleEvent = { kind: "circle", radius: number };
type Config = EventConfig<SquareEvent | CircleEvent>
type Config = {
square: (event: SquareEvent) => void;
circle: (event: CircleEvent) => void;
}
模板字面量
与javascript中的模板字面量字符串相同的语法,但用于类型位置,党羽具体字面类型一起使用时,模板字面量通过链接内容来生成新的字符串字面量类型
type World = "world";
type Greeting = `hello ${World}`;
//生成结果
type Greeting = "hello world"
可以由每个联合成员标识的每个可能的字符串字面的集合,建议大型字符串联合可以提前生成,较小的情况下才这么使用
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
//结果
type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"