Summarize some places that have spent time on TypeScript

  • 2021-12-05 05:17:39
  • OfStack

Record some places where you spent time on ts.

(Spit a slot first: stackoverflow really has everything, Baidu really can't use it)

Pit

Compatibility misunderstandings of as assertions, such as "a" as "b", do not report errors.

The behavior of interface and type (I thought I wrote the wrong type when I first met, and I was forced):


type Type = {
  key: "value"
}
interface Interface {
  key: "value"
}

type  It doesn't seem to make any difference. It's all true = Type extends Interface ? Type extends Interface ? true : false : false

type  Pit point  = {
  [key: string]:  Pit point 
} | string

type  Test <T> = T extends  Pit point  ? true : false
type  This is true =  Test <Type>
type  This is false =  Test <Interface>

There is an official explanation on github that such a pit was deliberately left. It is said that interface is extensible (automatic merge of the same name), so it is inconvenient to detect.

When using generics to implement the effect of function overloading, it is often necessary to use as to enforce assertions because generics do not have specific constraints in the implementation of functions.


// Almost this meaning, the following code is too lazy to actually test 🙃

//fns Is a functional index table, TFns Is the of the index table const Type 
function  Overload failure <T extends keyof TFns>(fn:T, params: Parameters<fns[T]>){
    fns[fn](...params)// In the implementation, the union type will not shrink, so an error will be reported 
    // Errors should be like   You cannot put a method 1 Pass the parameters of to the method 2  This 
}
// However, when used externally, it is nothing to conform to the semantics of the type 

Extension operators are not intuitive: the type [... string [], number] is intuitive when used (requiring an number element at the end of the array), but the type [... string [], null,... object [], number] does not, does not come in sequence, and does not report errors. The new version of ts adds a rule prohibiting continuous deconstruction, which directly prevents writing.

In fact, there is a solution here, but the written type is simply impossible to see (a few 10 lines, including a large number of extends as the type of if judgment), so the following paste code is not posted:


// Types required: [...number[], "middle-element", ...boolean[]] 
// The above writing is invalid, just indicate what the following type code is used for (implementing the type constraint indicated above) 

type Elem = number | boolean | "middle-element";

type Last<T extends any[]> = T extends [infer _]
  ? never
  : T extends [...infer _, infer Tl]
  ? Tl
  : never

type HandleEmpty<T extends any[], Data> = T['length'] extends 0 ? never : Data

type Validation<Params extends any[], Cache extends Elem[] = []> =
  Params extends []
  ? Cache['length'] extends 0
  ? never
  : Cache
  : Params extends [infer Fst, ...infer Rest]
  ? Cache extends []
  ? Fst extends number
  ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
  : never
  : Fst extends number
  ? Last<Cache> extends number
  ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
  : never
  : Fst extends "middle-element"
  ? Last<Cache> extends number
  ? HandleEmpty<Rest, Validation<Rest, [...Cache, Fst]>>
  : never
  : "middle-element" extends Cache[number]
  ? Fst extends boolean
  ? Validation<Rest, [...Cache, Fst]>
  : never
  : never
  : never

type IsNever<T> = [T] extends [never] ? true : false;

function check<
  Params extends Elem[],
  IsValid extends Validation<Params>
>(...arr: IsNever<IsValid> extends true ? [never] : [...Params]) {
  return arr
}

const  Normal  = check(1, 'middle-element', false)
const  Report an error  = check(false, "middle-element", 2)

Advanced operation

Object name remapping:


//{ "new-a":any; "new-b":any }
type  Remapping  = {
    [K in "a" | "b" as `new-${K}`]: any
}

Split of union type: Split of union type can be realized by using infer keyword.


//"a1"|"b2"
type  Successful splitting <_Keys = keyof { a: 1, b: 2 }> = _Keys extends infer K ?
    `${Extract<K, string>}${{ a: 1, b: 2 }[Extract<K, _Keys>]}`
    : never

// Note: (As of ts4.4.4 ) Direct `keyof Obj extends infer K` Unable to split union type for unknown reason (too lazy to check 😁 ). 
// The result is "a1"|"a2"|"b1"|"b2"
type  Split failed  = keyof { a: 1, b: 2 } extends infer K ?
    `${Extract<K, string>}${{ a: 1, b: 2 }[Extract<K, "a" | "b">]}`
    : never

Tuple type:

Actual (non-typed) parameters sometimes need to be explicitly defined as tuple types through as const. The tuple type can get the exact length from the tuple ["length"], not number. When tuple types are used through generic parameters, it is sometimes necessary to add a [] to be written as a tuple extends [] any [] to avoid being resolved to ordinary indefinite array types.

Recursive types: Recursion of array types can be achieved with... infer More.


type  Converter <T> = T extends string ? "str" : null
// Going in is a [string,number,string] When you come out, it will be ["str",null,"str"]
type  Recursion <
         Input source  extends any[],
         Internal type cache  extends any[] = []
    > =  Input source  extends [any, ...infer  Residual element ] ?
         Recursion < Residual element , [... Internal type cache ,  Converter < Input source [0]>]>
        :  Input source 

Fragmentary

& It can be used instead of extends for type, and interface has no difference except that it can merge types with the same name. ts has a wealth of built-in types, pick a few examples: ReturnType < Function type > Gets the type of return value of the function type. Uncapitalize < String > Lock the first letter of the input string type to lowercase (others include first uppercase, all lowercase, and all uppercase).

Beginners suggest going to official website to turn over documents.

After entering the ts pit, you can pay attention to the new features (gameplay) brought by the next version update.

Summarize


Related articles: