一、ts类型保护
1、内置类型保护
- typeof 类型保护
代码语言:ts复制使用 typeof 运算符来检查值的类型。
function isString(x: any): x is string {
return typeof x === "string";
}
- instanceof 类型保护
代码语言:ts复制使用 instanceof 运算符来检查值是否是某个类的实例。在这个例子中,
x is Dog
是一个类型保护,它告诉TypeScript编译器,如果 isDog 函数返回 true,那么 x 一定是 Dog 类型。
class Animal {
eat(): void {
console.log('Animal eats');
}
}
class Dog extends Animal {
bark(): void {
console.log('Dog barks');
}
}
function isDog(x: Animal): x is Dog {
return x instanceof Dog;
}
let pet: Animal = new Dog();
if (isDog(pet)) {
pet.bark();
} else {
pet.eat();
}
2、自定义类型保护
代码语言:ts复制当内置的类型保护不足以满足需求时,可以创建自定义的类型保护。这通常是通过编写一个返回类型保护签名的函数来实现的
type Fish = { swim: () => void; };
type Bird = { fly: () => void; };
function isFish(x: Fish | Bird): x is Fish {
return (x as Fish).swim !== undefined;
}
function handleAnimal(animal: Fish | Bird) {
if (isFish(animal)) {
animal.swim(); // 这里TypeScript知道animal是Fish类型
} else {
(animal as Bird).fly(); // 这里我们需要手动断言,因为TypeScript不知道else分支中animal是Bird类型
}
}
二、泛型
泛型(Generics)允许定义灵活的组件,这些组件可以工作于多种数据类型。通过使用泛型,可以创建可重用的组件,这些组件可以适应多种数据类型,而无需为每种数据类型都重新编写代码。
1、定义泛型
代码语言:ts复制泛型是通过在类型或函数名后面添加尖括号(
< >
)和类型参数来定义的。这些类型参数通常是大写字母(如T、U、V等),但它们可以是任何有效的标识符。
type A<T, U> = T | U;
type Arr<T>=T[]
function fn<T>(arg: T): T {
return arg;
}
function Fn<T>(n:T){
return n;
}
let a: A<string,number> = 'hello world'
let arr:Arr<number>=[1,2,3]
let output = fn<string>("xiaomu");
let output2 = fn(20);
let output3 = Fn('hello world'); // 这里不需要 <string>,因为 TypeScript 会自动推断,也是常用写法
let output4=Fn<string>('hello world')
- 泛型接口
interface Fn<T> {
(arg: T): T;
}
let identityFn: Fn<number> = (x) => x;
let result = identityFn(20); // result 的类型是 number,值为 20
console.log(result); // 输出 20
- 泛型类
class Foo<T> {
username!:T;
}
class Tfo extends FOO<string>{}
let f=new Foo<string>();
let tf=new Tfo()
f.username='xiaomu'
tf.username='extends'
- 多参数泛型类的使用
class Fn<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zeroValue: T, add: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = add;
}
zero(): T {
return this.zeroValue;
}
addNumbers(x: T, y: T): T {
return this.add(x, y);
}
}
let myFn= new Fn<number>(0, (x, y) => x y);
// 使用 zero 方法获取零值
let zero = myFn.zero();
console.log(zero); // 输出:0
// 使用 addNumbers 方法执行加法
let sum = myFn.addNumbers(5, 3);
console.log(sum); // 输出:8
2、泛型常见操作
- 类型参数
在泛型定义中,类型参数(如T)用于表示类型占位符,这些占位符将在使用泛型时由具体的类型来替换。
- 类型推断
在调用泛型函数或实例化泛型类时,TypeScript编译器会尝试根据提供的参数来推断类型参数。如果编译器无法推断出类型参数,可能需要显式地指定它们。
- 泛型约束
代码语言:ts复制可以使用extends关键字为泛型类型参数添加约束。这允许指定类型参数必须满足的接口或类型。
interface Fn {
length: number;
}
function Foo<T extends Fn>(arg: T): T {
console.log(arg.length); // 现在我们可以访问arg.length属性了
return arg;
}
- 泛型类型别名
代码语言:ts复制可以使用type关键字为泛型创建类型别名
type Fn<T> = { value: T };
let f: Fn<string> = { value: "Hello, world!" };
- 默认泛型类型
代码语言:ts复制在TypeScript 2.3及更高版本中,可以为泛型类型参数提供默认类型
//接受两个参数:length(表示数组的长度)和 value(表示数组中每个元素的值)
function Arr<T = string>(length: number, value: T = "default"): Array<T> {
let result: T[] = new Array(length);
for (let i = 0; i < length; i ) {
result[i] = value;
}
return result;
}
let a = Arr(3); // a 的类型是 string[]
let b = Arr<number>(3, 18); // b 的类型是 number[]
console.log(a); // 输出: ["default", "default", "default"]
console.log(b); // 输出: [42, 42, 42]
- 泛型数组
代码语言:ts复制泛型可以与数组一起使用,以创建可以包含任何数据类型的数组。
let list: Array<number> = [1, 2, 3];
let list2: number[] = [1, 2, 3];
- 泛型元组
代码语言:ts复制ypeScript 3.0引入了泛型元组类型,允许你创建具有特定数量和类型的元素的元组。
type Pair<T, U> = [T, U];
let pair: Pair<string, number> = ["hello world", 20];
- 泛型映射类型
代码语言:ts复制使用泛型映射类型,你可以基于一个已知的类型创建新的类型。
type Fn<T> = {
[P in keyof T]?: T[P];
};
type Foo= {
name: string;
sex: string;
age: number;
};
type f = Fn<Foo>; // { name?: string; sex?: string;age?: number; }