美文网首页
TypeScript

TypeScript

作者: bowen_wu | 来源:发表于2022-12-27 14:55 被阅读0次

    概述

    • 类型就是一组值的集合

    类型擦除

    TS 转化成 JS

    方法

    1. npm i -g esbuild => esbuild 1.ts > 1.js => 快(不检查 TS 语法)
    2. npm i -g @swc/cli @swc/core => swc 1.ts -o 1.js => 快(不检查 TS 语法)
    3. npm i -g typescript => tsc 1.ts

    数据类型

    • null/undefined
    • string/number/boolean
    • bigint/symbol
    • object
    • void
    • any/unknow
    • never
    • enum
    • 自定义类型 => type(类型别名)/interface

    TS 描述对象

    1. 使用 class/constructor 描述
    2. 使用 type/interface 描述
    type A = {
        // 索引签名
        [key: string]: number
    }
    
    // Record 泛型
    type B = Record<string, number>;
    
    const n: BigInt = 100n;
    console.log(n); // 100n
    const symbol: Symbol = Symbol("s");
    console.log(symbol); // Symbol(s)
    
    const date: Date = new Date()
    const regexp: RegExp = /ab+c/;
    const regexp1: RegExp = new RegExp("/\d+/");
    const map: Map<string, number> = new Map();
    map.set("a", 1);
    const set: Set<number> = new Set();
    set.add(1);
    

    TS 描述数组

    1. type A = string[] == type A = Array<string>
    2. 元组 => type A = [string, string, number] => length == 3
    3. type A = [1, 2, 3 | 4] => const a: A = [1, 2, 3]

    TS 描述函数

    1. type FnA = (a: number, b: number) => string => const a: FnA = () => "hello world";
    type FnReturnVoid = () => void;
    type FnReturnUndefined = () => undefined;
    
    const f1: FnReturnVoid = () => {
        console.log("hi");
    }
    
    const f2: FnReturnUndefined = () => {
        console.log("hi");
    
        // 此处需要显式声明
        return undefined;
    }
    
    type Person = {
        name: string,
        age: number,
        sayHi: FnWithThis
    }
    type FnWithThis = (this: Person, name: string) => void;
    
    // 此处如果使用箭头函数,则 this 指向有问题
    const sayHi: FnWithThis = function () {
        console.log("hi " + this.name)
    }
    
    const ming: Person = {
        name: "ming",
        age: 18,
        sayHi: sayHi
    }
    
    ming.sayHi("Jack");
    sayHi.call(ming, "Jack");
    

    any vs unknown

    • any => 全集 => 不做任何类型检查
    • any 不等于所有类型的联合 => 反证法:联合类型如果没有区分类型,只能使用交集的属性|方法
    • TS 绝大部分规则对 any 不生效 => const fn = (a: any) => {const b: never = a;} => 报错
    • unknown => 未知集 => 使用时需要先收窄类型 => 所有类型的联合(联合类型)

    never

    • 空集
    • 不应该出现的类型

    enum

    1. 使用一个有意义的名字代表一个状态
    enum A {
        TODO = 0,
        DONE,
        ARCHIVED,
        DELETED
    }
    
    // 类型擦除时 A.DONE 会变成 1
    let status: A = A.DONE;
    
    
    1. 通过使用位运算可以很方便的将权限进行组合和对比
    enum Permission {
        NONE = 0, // 0000
        READ = 1 << 0, // 0001
        WRITE = 1 << 1, // 0010
        DELETE = 1 << 2, // 0100
        MANAGE = READ | WRITE | DELETE // 0111
    }
    
    type User = {
        permission: Permission
    }
    
    const user: User = {
        permission: 0b0011
    }
    
    if ((user.permission & Permission.WRITE) === Permission.WRITE) {
        console.log("拥有写权限")
    }
    
    1. 使用 enum 做字符串映射字符串时可以使用类型别名代替
    // enum Status {
    //     done = 'done',
    //     process = 'process',
    //     start = 'start'
    // }
    
    type Status = 'done' | 'process' | 'start';
    

    type

    • 类型别名
    • 声明一个别名,并不是一个新的类型
    • type 的值都是类型 => type 的值里面不能有任何值 => type nan = NaN // 这是错误的 => 'NaN' refers to a value, but is being used as a type here
    • type A = {name: string} => 表示 name 为 string 的对象
    type A = { name: string };
    const a1 = { name: 'ming', age: 18 };
    
    // 此时 TS 不会做严格检查
    const a2: A = a1;
    
    // Error => TS 第一次声明的时候会进行严格检查
    const a: A = { name: 'ming', age: 18 }
    
    // 其中的 0、null、undefined、''、false 都是类型
    type Falsy = 0 | null | undefined | '' | false;
    
    // 类型A是一个集合,集合中只有一个0
    type A = 0;
    
    type FnWithProp = {
        (a: number, b: number): number,
        props1: number;
    }
    
    const fn: FnWithProp = (x, y) => x + y;
    fn.props1 = 101;
    
    console.log(fn);
    console.log(fn.props1)
    

    interface

    • 声明接口
    • 描述对象的属性 => declare the shapes of object

    type vs interface

    1. interface 只描述对象,type 则描述所有类型
    2. interface 是类型声明,type 是别名
      type A = string;
      type B = A; // B is string
      
      interface C extends Date {
      }
      
      type D = C; // D is C
      
    3. interface 可以进行 merging(扩展),type 不可以重新赋值 => 对外 API 尽量使用 interface,方便扩展。对内 API 尽量使用 type(case: props),防止代码分散
    4. interface extends 时如果属性冲突,直接报错,type 会联合类型到 never

    Object vs object

    • Object 包含 Number/String/Boolean 包装类型
    • object 不包含包装类型

    void vs null vs undefined

    • void 不可以赋值给 null 和 undefined
    • undefined 可以赋值给 void
    • 其余需要看配置 => strictNullChecks
    let a: void
    
    // 此时 void = null | undefined
    if (typeof a === 'object') {
        // a is null
        console.log(a);
    } else if (typeof a === 'undefined') {
        // a is undefined 
        console.log(a);
    } else {
        // a is never 
        console.log(a);
    }
    
    // 此时 void = undefined
    if (typeof a === 'undefined') {
        // a is undefined
        console.log(a);
    } else if (typeof a === 'object') {
        // a is never 
        console.log(a);
    } else {
        // a is never 
        console.log(a);
    }
    

    Assignment

    Assignment

    TS 类型系统运算

    联合类型

    • union types => 并集

    JS 语言特性区分类型

    • 使用 JS 的语言特性 typeof + instanceof + in 进行类型收窄
    const fn = (a: number | string) => {
        // 类型收窄 => Narrowing
        if (typeof a === 'number') {
            a.toFixed(2);
        } else if (typeof a === 'string') {
            a.split(',');
        } else {
            // a === never
            console.log(a);
        }
    }
    
    type Person = {
        name: string;
    }
    
    type Animal = {
        x: string;
    }
    
    const f1 = (param: Person | Animal) => {
        // 使用 in 进行类型收窄
        if ('name' in param) {
            console.log(param.name);
        } else {
            console.log(param.x);
        }
    }
    

    类型谓词 is

    type Rect = {
        height: number,
        width: number
    }
    
    type Circle = {
        center: [ number, number ],
        radius: number
    }
    
    const fn = (a: Rect | Circle) => {
        if (isRect(a)) {
            // a is Rect
            console.log(a.height);
        } else if (isCircle(a)) {
            // a is Circle
            console.log(a.radius);
        } else {
            // a is never
            console.log(a);
        }
    }
    
    function isRect(x: Rect | Circle): x is Rect {
        return 'height' in x && 'width' in x;
    }
    
    // 此处 x is Circle 不能写在左侧 => const isCircle: (x: Rect | Circle) => x is Circle = x => 'radius' in x && 'center' in x;
    const isCircle = (x: Rect | Circle): x is Circle => {
        return 'radius' in x && 'center' in x;
    }
    

    可辨别联合 Discriminated Unions

    • 让复杂类型的收窄变成简单类型的对比
    type Circle = { kind: "Circle", center: [ number, number ] };
    type Square = { kind: "Square", sideLength: number };
    type Shape = Circle | Square;
    
    const f1 = (a: string | Shape) => {
        if (typeof a === 'string') {
            // a is string
            a.split(',');
        } else if (a.kind === 'Circle') {
            // a is Circle
            console.log(a.center);
        } else {
            // a is Square
            console.log(a.sideLength);
        }
    }
    

    断言 as

    // a is string
    let a = 'hi';
    
    // b is hi
    const b = 'hi';
    
    // c is hi
    let c = 'hi' as const;
    
    // array is (number | string)[] => 对于引用类型在 TS 中 const 相当于 let
    const array = [ 1, 'hi' ];
    
    // array1 is readonly [1, 'hi']
    const array1 = [ 1, 'hi' ];
    
    // Error: Property 'push' does not exist on type 'readonly [1, "hi"]'.
    array1.push(2);
    

    交叉类型 intersection types

    • 交叉类型常用于有交集的类型 A B
    • 如果 A、B 无交集,可能得到 never,也可能属性为 never

    type

    type Left = {
        left: string;
    }
    
    type Right = {
        right: string;
    }
    
    type C = Left | Right;
    type D = Left & Right;
    const c: C = {
        left: "left"
    }
    const d: D = {
        left: 'left',
        right: 'right'
    }
    
    type Person = {
        name: string;
        age: number;
        id: string
    }
    
    // 此处 id 属性的类型是 string & number => id is never
    type User = Person & {
        id: number;
        email: string;
    }
    
    const user: User = {
        id: 1 as never,
        name: 'ming',
        age: 18,
        email: 'email'
    }
    
    // userIsNever is never
    type UserIsNever = { id: 'A' } & { id: 'B' };
    

    interface

    interface Colorful {
        color: string;
    }
    
    interface Circle {
        radius: number
    }
    
    type ColorfulCircle = Colorful & Circle;
    
    interface Person {
        id: string;
        name: string;
    }
    
    interface User extends Person {
        // Error: type 'number' is not assignable to type 'string'.
        // id: number;
        email: string;
    }
    

    神奇

    type A = {
        method: (n: number) => void;
    }
    
    type B = {
        method: (n: string) => void;
    } & A;
    const b: B = {
        // n is any
        method: (n) => {
            console.log(n);
        }
    }
    
    type F1 = (n: number) => void;
    type F2 = (n: string) => void;
    type X = F1 & F2;
    const x: X = (n) => {
        // n is any
        console.log(n);
        n.split(',')
        n.toFixed(2);
    }
    

    类型兼容 & 赋值

    基本类型

    type A = string | number;
    const a: A = 'hi';
    

    普通对象

    type Person = {
        name: string;
        age: number;
    }
    
    let user = { name: 'ming', age: 18, id: 1 };
    let p: Person;
    p = user;
    // type User = {name: string; age: number; id: nubmer}
    type User = typeof user;
    

    接口

    interface Parent {
        x: string;
    }
    
    interface Sub extends Parent {
        y: string;
    }
    
    let sub: Sub = {
        x: '1',
        y: '2'
    };
    
    let p: Parent;
    p = sub;
    

    函数

    • 函数包括参数返回值
    • 参数少的函数可以赋值给参数多的函数
    • 对参数要求少函数可以赋值给对参数要求多的函数

    深入对象语法

    索引签名 Index Signature

    type Hash = {
        [k: string]: unknown;
        length: number;
    }
    

    映射类型 Mapped Type

    • 多用于泛型
    type Hash = {
        // 不能再写其他属性
        [key in string]: unknown;
    
        // length: number; => error
    }
    

    只读属性 readonly

    interface User {
        readonly id: number;
        age?: number;
    }
    
    const user: User = {
        id: 1,
        age: 18,
    }
    // Error: Cannot assign to 'id' because it is a read-only property
    user.id = 2;
    

    深入函数语法

    对象语法全部适用于函数

    type F = {
        (a: number, b: number): number;
        readonly count?: number;
    }
    
    type F1 = (a: number, b: number) => number;
    
    const f: F = (x, y) => x + y;
    

    声明函数及其类型

    // First
    type F1 = (a: number, b: number) => number;
    const f1: F1 = (a, b) => a + b;
    
    // Second
    const f2 = (a: number, b: number): number => a + b;
    type F2 = typeof f2;
    
    // Third
    function f3(this: unknown, a: number, b: number): number {
        return a + b;
    }
    
    type F3 = typeof f3;
    
    // Fourth
    const f4 = function (this: unknown, a: number, b: number): number {
        return a + b;
    }
    type F4 = typeof f4;
    

    类型谓词

    可选参数

    参数默认值

    function addEventListener(eventType: string, fn: (this: HTMLElement, e: Event) => void, useCapture = false) {
        const element = {} as HTMLElement;
        const event = {} as Event;
        fn.call(element, event);
    }
    
    addEventListener('click', () => console.log(1));
    

    重载 overload

    • overload => 同名函数参数不同
    • 参数类型不同 => 联合类型
    • 参数个数不同 => 考虑是用 overload
    • 参数类型和个数都不同
    function createDate(timestamp: number): Date;
    function createDate(year: number, month: number, day: number): Date;
    function createDate(a: number, b?: number, c?: number): Date {
        if (a !== undefined && b !== undefined && c !== undefined) {
            return new Date(a, b, c);
        } else if (a !== undefined && b === undefined && c === undefined) {
            return new Date(a);
        } else {
            throw new Error('Params is invalid!');
        }
    }
    
    // Error: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
    createDate(2022, 10);
    

    展开参数

    function sum(...array: number[]) {
        // Error: Argument of type 'number[]' is not assignable to parameter of type 'number'.
        // f(array)
        f(...array);
        f.apply(null, array);
    }
    
    function f(...array: number[]) {
        console.log(array);
    }
    

    as const

    function add(a: number, b: number): number {
        return a + b;
    }
    
    // arr is number[]
    const arr = [ 1, 2 ];
    
    // Error: A spread argument must either have a tuple type or be passed to a rest parameter.
    add(...arr);
    
    // arr1 is readonly [1, 2]
    const arr1 = [ 1, 2 ] as const;
    add(...arr1);
    

    解构

    type Config = {
        url: string;
        method: "GET" | "POST" | "PUT" | "DELETE";
        body?: unknown;
        headers?: unknown
    }
    
    // rest is {body?: unknown; headers?: unknown}
    function ajax({ url, method, ...rest }: Config = { url: '', method: "GET" }) {
    }
    
    function ajax1({ url, method, ...rest } = { url: '', method: "GET" } as Config) {
    }
    

    泛型 Generic Type

    • 推后执行的、部分待定的类型
    type F<A, B> = A | B;
    
    // Result is number | string
    type Result = F<number, string>;
    
    function echo(msg: number | boolean | string) {
        return msg;
    }
    
    // F is (msg: number | boolean | string) => number | boolean | string;
    type F = typeof echo;
    

    类型参数默认值

    interface Hash<V = string> {
        [k in string]: V;
    }
    

    条件类型 Conditional Type

    • 以下两条仅对泛型有效
    • 如果 T 是 never,则表达式的值为 never
    • 如果 T 是联合类型,则分开计算
    type LikeString<T> = T extends string ? true : false;
    
    type R1 = LikeString<'hi'>; // true
    type R2 = LikeString<true>; // false
    type X = LikeString<never>; // never
    type Y = LikeString<string | number>; // Y is boolean
    
    type ToArray<T> = T extends unknown ? T[] : never;
    type Result = ToArray<number | string>; // Result is number[] | string[]
    type Result1 = ToArray<never>; // Result1 is never
    
    type Z = never extends unknown ? 1 : 2; // Z is 1
    
    // 此处不能对应 string -> string[]  number -> number[] 
    type U = string | number extends unknown ? string[] | number[] : 1;
    

    keyof

    type Person = { name: string, age: number };
    type GetKeys<T> = keyof T;
    type Result = GetKeys<Person>; // Result is name | age
    

    泛型约束

    type GetKeyType<T, K extends keyof T> = T[K];
    type Result1 = GetKeyType<Person, "name">; // string
    type Test = Person['name']; // Test is string
    

    内置

    Readonly

    type Person = { name: string, age: number };
    // type Result = {
    //   readonly name: string;
    //   readonly age: number;
    // }
    type Result = Readonly<Person>;
    // type Readonly<T> = {
    //     readonly [k in keyof T]: T[k];
    // }
    

    Partial

    type Person = { name: string, age: number };
    // type Result = {
    //     name?: string | undefined;
    //     age?: number | undefined;
    // }
    type Result = Partial<Person>;
    // type Partial<T> = {
    //   [k in keyof T]?: T[k];
    // }
    

    Required

    type Person = { name: string, age: number, email?: string };
    // type Result = {
    //   name: string;
    //   age: number;
    //   email: string;
    // }
    type Result = Required<Person>;
    // type Required<T> = {
    //   [k in keyof T]-?: T[k];
    // }
    

    Record

    type Result = Record<string, number>;
    // type Record<K extends string | number | symbol, T> = {
    //   [k in K]: T;
    // }
    

    Exclude

    type Result = Exclude<1 | 2 | 3, 1 | 2>; // Result is 3
    // type Exclude<T, K> = T extends K ? never : T;
    

    Extract

    type Result = Extract<1 | 2 | 4, 2 | 3>; // Result is 3
    // type Extract<T, K> = T extends K ? T : never;
    

    Pick

    type Person = { name: string, age: number, email?: string };
    // type Result = {
    //   name: string | number;
    //   age: string | number;
    // }
    type Result = Pick<Person, 'name' | 'age'>;
    // type Pick<T, K extends keyof T> = {
    //   [key in K]: T[K];
    // }
    

    Omit

    type Person = { name: string, age: number, email?: string };
    // type Result = {
    //     email?: string | undefined;
    // }
    type Result = Omit<Person, 'name' | 'age'>;
    // type Omit<T, K> = {
    //     [k in keyof T as (k extends K ? never : k)]: T;
    // }
    
    // 使用 Pick
    // type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
    

    -readonly

    type Person = { name: string, age: number, email?: string };
    // type Result = {
    //   name: Readonly<Person>;
    //   age: Readonly<Person>;
    //   email?: Readonly<Person> | undefined;
    // }
    type Result = Mutable<Readonly<Person>>;
    type Mutable<T> = {
        -readonly [key in keyof T]: T;
    }
    

    class

    class Point {
        x: number;
        y: number;
    
        constructor(x = 0, y = 0) {
            this.x = x;
            this.y = y;
        }
    }
    
    // 等价于
    class Point1 {
        constructor(public x = 0, public y = 0) {
    
        }
    }
    
    class Hash {
        [key: string]: unknown;
    
        set(key: string, value: unknown) {
            this[key] = value;
        }
    
        get(key: string) {
            return this[key];
        }
    }
    

    constructor reload

    class Point {
        x!: number;
        y!: number;
    
        constructor(x: number, y: number);
        constructor(str: string);
        constructor(xs: number | string, y?: number) {
            if (typeof xs === 'number' && typeof y == 'number') {
                this.x = xs;
                this.y = y;
            } else if (typeof xs === 'string') {
                this.x = parseFloat(xs.split(',')[0]);
                this.x = parseFloat(xs.split(',')[1]);
            }
        }
    }
    

    implements

    • implements 仅仅会做类型检查,其余不会进行任何操作
    interface Person {
        name: string;
        sayHi: (target: Person) => void;
    }
    
    interface Taggable {
        tags: string[];
        addTag: (tag: string) => void;
        removeTag: (tag: string) => void;
    }
    
    class User implements Person, Taggable {
        constructor(public name: string, public tags: string[] = []) {
        }
    
        sayHi(target: Person) {
            console.log('Hi ' + target.name);
        }
    
        addTag(tag: string) {
            this.tags.push(tag);
        }
    
        removeTag(tag: string) {
            this.tags.splice(this.tags.indexOf(tag), 1);
        }
    }
    
    const ming = new User('ming');
    ming.sayHi(ming);
    

    declare

    • 重写属性
    class Person {
        friend?: Person;
    
        constructor(public name: string, friend?: Person) {
            this.friend = friend;
        }
    }
    
    class User extends Person {
        // Error: Property 'friend' will overwrite the base property in 'Person'. If this is intentional, add an initializer. Otherwise, add a 'declare' modifier or remove the redundant declaration.
        // friend?: User;
        declare friend?: User;
    
        constructor(public id: string, name: string, friend?: User) {
            super(name, friend);
        }
    }
    
    const u1 = new User("1", "jack");
    
    const u2 = new User("2", "george", u1);
    
    // 如果没有 declare => Friend is Person | undefined
    // 如果有 declare => Friend is User | undefined
    type Friend = typeof u2.friend;
    

    public & private & protected

    • public => 类外可见
    • private => 类内可见 => 类型擦除之后就没有 private 了,在 js 中仍然可以使用该属性
    • protected => 子类可见
    • #<var> => 真私有属性 => 类型擦除之后会保留,在 js 中不可使用该属性

    static

    • 通过类型访问
    • 类属性/静态属性
    • 不能有 static name => 类是使用函数模拟的,函数本身有 name 属性
    • static block
    class Foo {
        static #count = 0;
        static {
            const count = loadFromLocalStorage() || 0;
            Foo.#count += count;
        }
    
        constructor() {
            console.log(Foo.#count);
        }
    }
    

    抽象类

    • 抽象类不能直接实例化
    abstract class A {
        abstract name: string;
        age: string;
    
        constructor(age: string) {
            this.age = age;
        }
    
        abstract getName(): string;
    
        printName() {
            console.log("Hi, " + this.getName());
        }
    }
    

    类作为参数

    class Person {
        constructor(public name: string) {
            this.name = name;
        }
    }
    
    function f(X: typeof Person) {
        const p = new X('George');
    }
    
    f(Person)
    
    // new 表示传入的参数必须是 class
    function f2(X: new (name: string) => Person) {
        const p = new X("George");
    }
    
    f2(Person);
    

    类型体操

    // if (A <= B) true else false
    type A = 1;
    type B = 1 | 2;
    
    type Result = A extends B ? true : false;
    
    // 空元组
    type A = [];
    type IsEmptyArray<Arr extends unknown[]> = Arr['length'] extends 0 ? true : false;
    
    type Result = IsEmptyArray<[]>;
    
    // 非空元组
    type B = [ 1 ];
    type NotEmpty<Arr extends unknown[]> = Arr extends [ ...unknown[], unknown ]
        ? true
        : false;
    type NotEmpty1<Arr extends unknown[]> = Arr extends [ ...infer X, infer Last ]
        ? true
        : false;
    
    type Result1 = NotEmpty<B>;
    

    递归

    • TS 最多递归 48 层
    type A = [ 1, 2, 3, 4 ];
    type Reverse<Arr extends unknown[]> = Arr extends [ ...infer Rest, infer Last ] ? [ Last, ...Reverse<Rest> ] : Arr;
    
    type Result = Reverse<A>;
    

    模式匹配 + infer

    • infer => decoratively introduce a new generic type variable
    type A = [ 1, 2, 3, 4 ];
    
    // Result1 is 1
    type Result1 = A extends [ infer First, ...infer Rest ] ? First : never;
    
    // Result2 is [2, 3, 4]
    type Result2 = A extends [ string, ...infer Rest ] ? Rest : never;
    

    元组体操

    // 将元组加长
    type A = [ 1 ];
    type B = [ ...A, number ];
    type C = [ ...B, 'hi' ];
    type D = [ ...B, ...C ];
    
    // get Last type in Tuple
    type Last<T extends unknown[]> = T extends [ ...unknown[], infer Last ] ? Last : never;
    type Hi = Last<C>; // Hi is 'hi'
    
    // Error => TS 没有减法
    // type Last<T extends unknown[]> = T[T["length"] - 1];
    
    
    
    

    字符串体操

    Capitalize & Uncapitalize & Uppercase & Lowercase

    • TS 内部实现
    • Capitalize => 首字母大写
    • Uncapitalize => 首字母小写
    • Uppercase => 全部大写
    • Lowercase => 全部小写
    type A = "george";
    type B = Capitalize<A>; // B is George
    
    type C = 'hi' | 'george';
    type D = Capitalize<C>; // D is 'Hi' | 'George'
    

    模板字符串

    type A = "Hi";
    type B = "George";
    
    type Result = `${A} ${B}`;
    

    模式匹配

    type A = "Hi George";
    type First<T extends string> = T extends `${infer F}${string}` ? F : never;
    type A = "Hi George";
    
    type LastOfTuple<T extends unknown[]> = T extends [ ...infer _, infer L ] ? L : never;
    
    // String to Tuple
    type StringToTuple<S extends string> = S extends `${infer F}${infer Rest}` ? [ F, ...StringToTuple<Rest> ] : [];
    type Last<T extends string> = LastOfTuple<StringToTuple<T>>;
    
    type Result = First<A>; // Result is H
    type ResultOfLast = Last<A>; // Result is e
    

    String to Union

    • 联合类型自动去重
    type A = "Hi George";
    type StringToUnion<S extends string> = S extends `${infer First}${infer Rest}` ? First | StringToUnion<Rest> : never;
    type Result = StringToUnion<A>;
    

    TSX

    • 在 TSX 中标签式断言会和标签冲突,此时使用 as 断言

    知识点

    1. 在 TSX 中 input 的 onChange 实际上调用的是 oninput 事件
      • onchange => 触发时机是失去焦点时
      • oninput => 触发时机是输入时

    相关文章

      网友评论

          本文标题:TypeScript

          本文链接:https://www.haomeiwen.com/subject/shgyxdtx.html