Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript Tricks #123

Open
xianshenglu opened this issue Feb 10, 2021 · 2 comments
Open

Typescript Tricks #123

xianshenglu opened this issue Feb 10, 2021 · 2 comments

Comments

@xianshenglu
Copy link
Owner

xianshenglu commented Feb 10, 2021

How to use recursion

Add generic params to pass recursive params.

Example:

How to count for Array

Use the length of the Array instance. Examples:

How to transform number to string

Use the template literal and infer.

Example: Absolute

How to transform string to number

Use string to compare and Array to count.

Example:

type ToNumber<S extends string, A extends any[] = []> = `${A['length']}` extends S
  ? A['length'] : ToNumber<S, [any, ...A]>
ToNumber<'0'> // 0
ToNumber<'10'> // 10

How to access some parts of string or Array

Use multiple infer keywords.

Example: StringToUnion

How to detect if a string or input value needs transformation

Compare the input with the output using extends.

Example:

type isCapitalizable<S extends string> = S extends Capitalize<S> ? false : true
type foo = isCapitalizable<'Foo'> //false
type bar = isCapitalizable<'bar'> //true

How to add transformation except for the first time

Set default value in generic params.

Example:

type KebabCase<S, P extends string = ''> = S extends `${infer F}${infer L}`
  ? (F extends Lowercase<F> ? `${F}${KebabCase<L, '-'>}` : `${P}${Lowercase<F>}${KebabCase<L, '-'>}`) : S
KebabCase<'FooBarBaz'> // 'foo-bar-baz'
How to test every item type in Array like some and every?

Use Array[number].

Example:

type AnyOf<T extends any[]> = T[number] extends 0 | '' | false | [] | {[key: string]: never}
? false : true;
AnyOf<[0, '', false, [], {}] //false
AnyOf<[0, '', false, [], {x: number}] //true
AnyOf<[0, '', false, [1], {}] //true
infer match mode
type s0 =`` extends `${infer A}${infer B}${infer C}`?C:false //false
type s1 =`a` extends `${infer A}${infer B}${infer C}`?C:false //false
type s2 =`ab` extends `${infer A}${infer B}${infer C}`?C:false // ''
type s3=`abcd` extends `${infer A}${infer B}${infer C}`?C:false //'cd'
type s4=`ab_c` extends `${infer A}_${infer B}${infer C}`?C:false // ''
infer as generic params

Normally, we would use infer like

type GetTypeVal1<T> = T extends { type: infer R }
  ? R : T extends { typeName: infer R2 } ? R2 : T

type TypeVal11 = GetTypeVal1<{ type: string }>
type TypeVal12 = GetTypeVal1<{ type: string, typeName: number }>

However, one day I saw another usage of infer like this

type GetTypeVal2<T> = { type: T } | T | { typeName: T }
type TypeVal21 = { type: string } extends GetTypeVal2<infer T> ? T : any //string
type TypeVal22 = { type: string, typeName: number } extends GetTypeVal2<infer T> ? T : any //string | number

It describes the situations first and then uses the infer as the generic params to get the matched types. A little different from GetTypeVal1. Also, we can change GetTypeVal1 to work like GetTypeVal2.

type GetTypeVal1<T> = T extends { type: infer R, typeName: infer R2 }
  ? R | R2 : T extends { type: infer R3 } ? R3 : T extends { typeName: infer R4 } ? R4 : T

type TypeVal11 = GetTypeVal1<{ type: string }> //string
type TypeVal12 = GetTypeVal1<{ type: string, typeName: number }>  //string | number

Compared with infer in generic params, wouldn't that easier?

However, normally this is not what we want. Also, there is a related problem to look out for. microsoft/TypeScript#32389

How to add properties to an object, items to an array, or union recursively?
type TupleKeys<T extends any[], R extends number[] = []> = T extends [infer S1, ...infer S2]
  ? TupleKeys<S2, [S2['length'], ...R]> : R

type Index = TupleKeys<['macOS', 'Windows', 'Linux']> // [0,1,2]
type TupleKeys<T extends any[]> = T extends [infer S1, ...infer S2]
  ? S2['length'] | TupleKeys<S2> : never
type Index = TupleKeys<['macOS', 'Windows', 'Linux']> // 0 | 2 | 1
type TupleKeys<T extends any[]> = T extends [infer S1, ...infer S2]
  ? {
    [P in `${S2['length']}`]: S2['length']
  } & TupleKeys<S2> : {}

type Index= TupleKeys<['macOS', 'Windows', 'Linux']> // {2:2,1:1,0:0}
Utilities
// type IsArrayEmpty<T extends []> = T["length"] extends 0 ? true : false
// type IsObjectEmpty<T extends {}> = keyof T extends never ? true : false

type IsArrayEmpty<T extends []> = T extends [] ? true : false
type IsObjectEmpty<T extends {}> = T extends { [key: string]: never } ? true : false
@xianshenglu
Copy link
Owner Author

xianshenglu commented Mar 5, 2021

@xianshenglu
Copy link
Owner Author

@JK-0 Seriously?

@github-staff github-staff deleted a comment from JK-0 Apr 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
@xianshenglu and others