交叉类型是将多个类型合并为一个类型
交叉类型通常用于类型扩展,确保一个类型同时具有多个类型的特性
交叉类型使用 &
符号来定义
语法:
T1 & T2 & ... & Tn
示例:
interface A {
a: string;
}
interface B {
b: number;
}
type C = A & B;
const c: C = {
a: 'hello',
b: 42,
};
联合类型表示一个值可以是几种类型之一
联合类型通常用于类型保护,确保一个类型属于多个类型中的一个
联合类型使用 |
符号来定义
语法:
T1 | T2 | ... | Tn
示例:
type Options = 'A' | 'B' | 'C';
const option: Options = 'A';
类型保护允许你在代码块中确保变量属于某个特定的类型
定义:
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
// 定义一个类型保护函数 isFish
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
使用:
// 'swim' 和 'fly' 调用都没有问题了
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
function getLength(arg: number| string): number {
if (typeof arg === 'string') {
return arg.length;
} else {
return arg.toString().length;
}
}
const curDate = new Date();
if (curDate instanceof Date) {
console.log(curDate.getHours());
}
类型别名会给一个类型起个新名字。
类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型
起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型
关键字:type
示例:
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
getName('ts')
getName(() => 'ts');
// getName(() => 123); // 报错
同接口一样,类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入:
type Container<T> = { value: T };
相同点:
1、都可以定义对象
interface Personi {
name: string;
age: number;
}
type Persont = {
name: string;
age: number;
}
2、都可以定义函数
interface addTypei {
(num1: number, num2: number): number;
}
type addTypet = (num1: number, num2: number) => number;
不同点:
1、interface 使用 extends 实现继承, type 使用交叉类型实现继承
interface 继承 interface:
interface Person {
name: string;
}
interface Student extends Person {
grade: number;
}
type 继承 type:
type Person = {
name: string;
}
type Student = Person & { grade: number } // 用交叉类型
interface 继承 type:
type Person = {
name: string;
}
interface Student extends Person {
grade: number;
}
type 继承 interface:
interface Person {
name: string;
}
type Student = Person & { grade: number } // 用交叉类型
2、interface可以合并重复声明,type 不行(重复声明 type ,就报错了)
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = {
name: 'ts',
age: 12,
}
3、应用场景:
一般使用组合或者交叉类型的时候,用 type
一般要用类的 extends 或 implements 时,用 interface
你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做可辨识联合的高级模式,它也称做标签联合或代数数据类型
分3步骤:
示例:
步骤1:
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
步骤2:
type Shape = Square | Rectangle | Circle;
步骤3:
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}
完整性检查
当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们。 比如,如果我们添加了 Triangle到 Shape,我们同时还需要更新 area:
type Shape = Square | Rectangle | Circle | Triangle;
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
// should error here - we didn't handle case "triangle"
}
解决办法:
使用 never类型,编译器用它来进行完整性检查:
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
default: return assertNever(s); // error here if there are missing cases
}
}
使用索引类型,编译器就能够检查使用了动态属性名的代码
先介绍两种新的类型操作符:
keyof
操作符可以用于获取某种类型的所有键
其返回类型是联合类型
比如:
interface Person {
name: string;
age: number;
}
let personProps: keyof Person; // 'name' | 'age'
T[K]
,表示接口 T 的属性 K 所代表的类型
示例:
interface IPerson {
name: string;
age: number;
}
let type1: IPerson['name'] = 'hello' // string
let type2: IPerson['age'] = 23 // number
console.log(type1) // hello
console.log(type2) // 23
索引类型查询操作符 (keyof T) 和索引访问操作符 (T[K]), 你可以在泛型约束中使用它们来访问另一个类型的属性的类型
示例1:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; // T[K] 类型的返回值
}
let obj = {
name: 'hello',
age: 23,
}
getProperty(obj, 'name'); // 正确
getProperty(obj, 'grade'); // 错误,grade 不在 obj 中
示例2:
传入key的数组,获取对应的值的数组
function getValues<T, K extends keyof T>(userInfo: T, keys: K[]): T[K][] {
return keys.map(key => userInfo[key]);
}
const userInfo = {
name: 'ts',
age: 12,
}
getValues(userInfo, ['name', 'age'])
// getValues(userInfo, ['sex', 'age']) // 这样当我们指定不在对象里的属性时,就会报错
在 TypeScript 中是一种基于现有类型生成新类型的方式,它通过一个已知的类型,映射出一个新的类型
映射类型的基本形式使用一个 for…in 这样的循环语法,遍历某个类型的属性键,在映射过程中,你可以为这些属性应用修饰符或其他类型转换
用来对联合类型实现遍历
type Person = "name" | "school" | "major";
type obj = {
[p in Person]: string;
}
Partial
将T
的所有属性映射为可选的
示例:
interface Person {
name: string;
age: number;
}
type PersonPartial = Partial<Person>;
// 使用 Partial 改造一下,就可以变成可选属性
let P1: PersonPartial = {};
type MyPartial<T> = {
[P in keyof T]?: T[P];
}
Readonly
将T
的所有属性映射为只读的
示例:
interface Person {
name: string;
age: number;
}
type PersonReadonly = Readonly<Person>;
let p1: PersonReadonly = {
name: 'hello',
age: 12
};
// p1.name = 'world' // 报错
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
}
Pick
用于抽取对象子集,挑选一组属性并组成一个新的类型
interface Person {
name: string;
age: number;
sex: string;
}
type PersonPick = Pick<Person, 'name' | 'age'>;
let p1: PersonPick = {
name: 'hello',
age: 12
};
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
用于创建一个对象类型,其属性键为 Keys,属性值类型为 Type
这使得我们能快速的构造一个具有固定类型值的对象,而键的部分则来自于一个联合类型或字面量类型
Record<Keys, Type>
示例1:
interface Person {
name: string;
age: number;
}
type PersonRecord = Record<string, Person>
let personMap: PersonRecord = {
p1: {
name: 'hello',
age: 12
}
}
示例2:
type PageInfo = {
title: string;
};
// 字符串字面量的联合类型
type Page = 'home' | 'about' | 'contact';
// Record 类型将 Page 类型的每个属性都映射到 PageInfo 类型
const nav: Record<Page, PageInfo> = {
home: { title: 'Home' },
about: { title: 'About' },
contact: { title: 'Contact' }
};
type Record<K extends keyof any, T> = {
[P in K]: T
}
使用条件类型在映射时添加逻辑
T extends U ? X : Y
// 若类型 T 可被赋值给类型 U,那么结果类型就是 X 类型,否则就是 Y 类型
// extends在这里不是用于类继承,而是表示一个条件类型查询,即“T 是否可以赋值给 U”
预定义的有条件类型:
Exclude
– 从T
中剔除可以赋值给U
的类型
示例:
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
let t1: T00 = 'b';
let t2: T00 = 'd';
// let t3: Test = 'a'; // 报错,不包含'a'
type T02 = Exclude<string | number | (() => void), Function>; // string | number
Exclude 原理
never
表示一个不存在的类型never
与其他类型的联合后,为其他类型type Exclude<T, U> = T extends U ? never : T
示例:
type T1 = Exclude<'a' | 'b' | 'c', 'a'>;
// 类型为 'b' | 'c',因为 'a' 可以赋值给 'a',所以被排除了。
type T2 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>;
// 类型为 'c',因为 'a' | 'b' 都可以赋值给 'a' | 'b',所以被排除了。
Extract
– 提取T中可以赋值给U的类型
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T02 = Extract<string | number | (() => void), Function>; // () => void
Extract 原理
type Extract<T, U> = T extends U ? T : never
NonNullable
– 从T
中剔除null
和`undefined
type T04 = NonNullable<string | number | undefined>; // string | number
type T05 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
ReturnType
– 获取函数返回值类型
type T10 = ReturnType<() => string>; // string
type T11 = ReturnType<(s: string) => void>; // void
type T12 = ReturnType<(<T>() => T)>; // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
function f1(s: string) {
return { a: 1, b: s };
}
type T14 = ReturnType<typeof f1>; // { a: number, b: string }
type T15 = ReturnType<any>; // any
type T16 = ReturnType<never>; // any
type T17 = ReturnType<string>; // Error
type T18 = ReturnType<Function>; // Error
InstanceType
– 获取构造函数类型的实例类型
class C {
x = 0;
y = 0;
}
type T20 = InstanceType<typeof C>; // C
type T21 = InstanceType<any>; // any
type T22 = InstanceType<never>; // any
type T23 = InstanceType<string>; // Error
type T24 = InstanceType<Function>; // Error