什么是TS类型保护、泛型

2024-08-11 09:53:06 浏览数 (1)

一、ts类型保护

1、内置类型保护

  • typeof 类型保护

使用 typeof 运算符来检查值的类型。

代码语言:ts复制
function isString(x: any): x is string {
    return typeof x === "string";
}
  • instanceof 类型保护

使用 instanceof 运算符来检查值是否是某个类的实例。在这个例子中,x is Dog 是一个类型保护,它告诉TypeScript编译器,如果 isDog 函数返回 true,那么 x 一定是 Dog 类型。

代码语言:ts复制
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、定义泛型

泛型是通过在类型或函数名后面添加尖括号(< >)和类型参数来定义的。这些类型参数通常是大写字母(如T、U、V等),但它们可以是任何有效的标识符。

代码语言:ts复制
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')
  • 泛型接口
代码语言:ts复制
interface Fn<T> {
    (arg: T): T;
}
let identityFn: Fn<number> = (x) => x;
let result = identityFn(20); // result 的类型是 number,值为 20
console.log(result); // 输出 20
  • 泛型类
代码语言:ts复制
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'
  • 多参数泛型类的使用
代码语言:ts复制
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编译器会尝试根据提供的参数来推断类型参数。如果编译器无法推断出类型参数,可能需要显式地指定它们。

  • 泛型约束

可以使用extends关键字为泛型类型参数添加约束。这允许指定类型参数必须满足的接口或类型。

代码语言:ts复制
interface Fn {
    length: number;
}
function Foo<T extends Fn>(arg: T): T {
    console.log(arg.length);  // 现在我们可以访问arg.length属性了
    return arg;
}
  • 泛型类型别名

可以使用type关键字为泛型创建类型别名

代码语言:ts复制
type Fn<T> = { value: T };
let f: Fn<string> = { value: "Hello, world!" };
  • 默认泛型类型

在TypeScript 2.3及更高版本中,可以为泛型类型参数提供默认类型

代码语言:ts复制
//接受两个参数: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];
  • 泛型元组

ypeScript 3.0引入了泛型元组类型,允许你创建具有特定数量和类型的元素的元组。

代码语言:ts复制
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; }

0 人点赞