laclys / Front-End_practice

Daily practice
5 stars 0 forks source link

Typescript4.0之后的新特性 #155

Open laclys opened 3 years ago

laclys commented 3 years ago

4.0 元组元素标签

type LabeledTupleType = [id: number, name: string];

可变元组

在元组类型的语法中,我们可以对泛型使用展开运算符 & 可以在元组中的任何位置使用剩余元素

const TupleA = ['A'] as const;
const TupleB = ['B'] as const;
type TupleType = readonly any[];
function concat<T extends TupleType, U extends TupleType>(arr1: T, arr2: U): [...T, ...U] { //  4.0版本之前会报错:ts(1256)
return [...arr1, ...arr2]; //  4.0版本之前会报错:ts(2741)
}
const TupleC = concat(TupleA, TupleB); // ['A', 'B'] // 合并两个元组为一个新的元组的函数!!! 从而得到了新的元组类型 ['A', 'B'] 
type ConcatedTuple = [ ...(typeof TupleA), ...(typeof TupleB)];

极大地提升函数式编程的类型体验和可能性

可以在函数组合中使用可变元组约束高阶函数入参和返回值的类型,比如对 JavaScript 内置 bind 方法更好地进行类型检测支持

4.1 模板字面量

Break Change🤓 使得字符串类型也具备了可变可运算的能力。

type PrefixType<P extends string, N extends string> = `${P}/${Capitalize<string & N>}`;
type UserLoginAction = PrefixType<'User', 'login'>; // 'User/Login'

可以对字符串类型进行拼接等操作 更加灵活,比如我们在定义路由的类型以及redux action type的类型都可以用到


// 比如将 { id: string; name: string} => “user/:id/:name/”

type RoutePath<P extends string, N extends string> = user/${P}/${N}/ type a = RoutePath<'123', 'Lac'> // "user/123/Lac/"

实现 `lodash get`
```typescript
type PropType<T, Path extends string> =
    string extends Path ? unknown :
    Path extends keyof T ? T[Path] :
    Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
    unknown;

declare function get<T, P extends string>(obj: T, path: P): PropType<T, P>;

const obj = { a: { b: {c: 42, d: 'hello' }}};

const value = get(obj, "a.b.c")

4.1 映射类型键名重新映射

在映射类型中,我们可以使用 as 操作符对键名重新映射(可以理解为针对类型的类型断言)

type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserInfoGetters = Getters<{ id:number; name: string; }> // { getId: () => number; getName: () => string; }

4.2元组头部/中间剩余元素

可以在元组的任何地方使用剩余元素表达式,而不再仅仅局限于元组的尾部

const prefixRestTuple: [...rest: string[], number] = ['a', 'b', 1];
const middleRestTuple: [boolean, ...rest: string[], number] = [true, 'a', 'b', 1];

4.2yield 表达式提示 noImplicitAny 错误

必须显式注解 yield 表达式的返回值类型,否则会提示 noImplicitAny 错误

这个还没有测试,如果使用Redux-saga管理effect副作用yield这块要注意

4.3增加了关键字 overrride

以保证基础类中的方法不会被覆盖

laclys commented 3 years ago

4.4 更智能的自动类型收窄

我们知道自动类型推导,只有收窄是安全的 eg:


function foo(arg: unknown) {
if (typeof arg === "string") {
// We know 'arg' is a string now.
console.log(arg.toUpperCase());
}
}
但是在4.4之前如果`我们将这个判定赋值给一个变量,再用到 if 分支里,就无法正常收窄类型了`
eg:
```typescript
function foo(arg: unknown) {
    const argIsString = typeof arg === "string";
    if (argIsString) {
        console.log(arg.toUpperCase());
        //              ~~~~~~~~~~~
        // Error! Property 'toUpperCase' does not exist on type 'unknown'.
    }
}

除此之外不仅是单一的判断,Typescript 4.4 还支持复合类型推导。4.4 支持了大部分符合直觉的推导非常方便

laclys commented 3 years ago

4.4 下标支持 Symbol 与模版字符串类型判定

interface Colors {
    [sym: symbol]: number;
}

const red = Symbol("red");
const green = Symbol("green");
const blue = Symbol("blue");

let colors: Colors = {};

colors[red] = 255;          // Assignment of a number is allowed
let redVal = colors[red];   // 'redVal' has the type 'number'

colors[blue] = "da ba dee"; // Error: Type 'string' is not assignable to type 'number'.

对于特定的字符串模版也支持类型匹配,比如希望以 data- 开头的下标是一种独立类型

interface Options {
    width?: number;
    height?: number;
}

let a: Options = {
    width: 100,
    height: 100,
    "data-blah": true, // Error! 'data-blah' wasn't declared in 'Options'.
};

interface OptionsWithDataProps extends Options {
    // Permit any property starting with 'data-'.
    [optName: `data-${string}`]: unknown;
}

let b: OptionsWithDataProps = {
    width: 100,
    height: 100,
    "data-blah": true,       // Works!

    "unknown-property": true,  // Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'.
};