Recommended reading before doing gymnastics:
- The official documentation
- Big guy’s article, benefit a lot
Before I was writing my own library, I felt that MY TS writing was too bad, so I decided to improve TS. I have to say that only by mastering these poses can I write better type design in application
Original project address: github.com/type-challe…
Besides, I’m very long, you can bear with me, if you really can’t help it, you can use it after Mark gets up
simple
Implement a pick
Implement the built-in Pick
for TS, but do not use it.
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
interface Todo {
title: string
description: string
completed: boolean
}
/* A = { title: string; completed: boolean; } * /
type A = MyPick<Todo, 'title' | 'completed'>
Copy the code
Realize the Readonly
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
Copy the code
Tuples are converted to objects
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type TupleToObject<T extends readonly string[]> = {
[P in T[number]]:P
}
const result: TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
Copy the code
Be careful to add readonly because as const generates the following type:
const tuple: readonly ["tesla"."model 3"."model X"."model Y"]
Copy the code
Const assertion document: www.typescriptlang.org/docs/handbo…
The first element
Implement a generic First
that takes an array T and returns the type of its First element.
type First<T extends any[]> = T extends [infer F, ...infer R] ? F : never
Copy the code
Tuples document: www.typescriptlang.org/docs/handbo…
Infer document: www.typescriptlang.org/docs/handbo…
Gets the length of the element
type Length<T extends readonly any[]> = T['length']
Copy the code
A tuple type is another Array type that knows exactly how many elements it contains and which types it contains at a particular location
Extend the performance of readOnly
Array, element plus readonly for common form parent set object attribute redonly does not affect type compatibility
type A = [string]
type RA = Readonly<A>
type B = string[]
type RB = Readonly<B>
type IsExtends<T, Y> = T extends Y ? true : false
type AExtendsRA = IsExtends<A, RA> //true
type RAExtendsA = IsExtends<RA, A> //false
type BExtendsRA = IsExtends<B, RB> // true
type RBExtendsB = IsExtends<RB, B> // false
type C = {
name: string
}
type RC = Readonly<C>
type CExtendsRC = IsExtends<C, RC> // true
type RCExtendsC = IsExtends<RC, C> // true
Copy the code
Object read-only properties do not affect type compatibility:
www.typescriptlang.org/docs/handbo…
Stackoverflow.com/questions/5…
Arrays and tuples are read-only:
Github.com/Microsoft/T…
Exclude
Implement the built-in Exclude <T, U>
type MyExclude<T, K> = T extends K ? never : T
Copy the code
Awaited
Get the ExampleType in the Promise
type Awaited<T extends Promise<any>> = T extends PromiseLike<infer L> ? L :never
Copy the code
If
example:
type A = If<true.'a'.'b'> // expected to be 'a'
type B = If<false.'a'.'b'> // expected to be 'b'
Copy the code
type If<C extends boolean, T, F> = C extends true ? T : F
Copy the code
Concat
example:
type Result = Concat<[1], [2] >// expected to be [1, 2]
Copy the code
type Concat<T extends any[], U extends any[]> = [...T, ...U]
Copy the code
Includes
example :
type isPillarMen = Includes<['Kars'.'Esidisi'.'Wamuu'.'Santana'].'Dio'> // expected to be `false`
Copy the code
// Convert a tuple to an object with value true
type Includes<T extends any[], U> = {
[K in T[number]] :true
}[U] extends true
? true
: false
Copy the code
medium
Gets the function return type
Implement TypeScript’s ReturnType
paradigm without using ReturnType.
type MyReturnType<T> = T extends(... argv:any[]) => infer T ? T :never
Copy the code
Implement Omit
// one
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
type MyExclude<T, K> = T extends K ? never : T
type MyOmit<T, K> = MyPick<T, MyExclude<keyof T, K>>
// two
type MyExclude<T, K> = T extends K ? never : T
type MyOmit<T, K> = {
[P in keyof T as P extends MyExclude<keyof T, K> ? P : never]: T[P]
}
Copy the code
Realize the Readonly 2
Implement a generic MyReadonly2
with two types of arguments T and K.
K specifies the property set of T that should be set to Readonly. If K is not provided, all properties should be made read-only, just like normal Readonly
.
type MyReadonly2<T, K=keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
} &
{
readonly [P in keyof T as P extends K ? P : never]: T[P]
}
// upgrade
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P]
} & T;
Copy the code
The depth of the Readonly
Implement a generic DeepReadonly
that recursively sets each parameter of the object and its children to read-only.
type IsObjectLiteral<T> = keyof T extends never ? false : true
type DeepReadonly<T> = {
readonly [P in keyof T]: IsObjectLiteral<T[P]> extends true
? DeepReadonly<T[P]>
: T[P]
}
Copy the code
Tuple to set
example:
type Arr = ['1'.'2'.'3']
const a: TupleToUnion<Arr> // expected to be '1' | '2' | '3'
Copy the code
type TupleToUnion<T extends any[]> = T[number]
Copy the code
Seriable constructor
example:
declare const config: Chainable
const result = config
.option('foo'.123)
.option('name'.'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// The expected result type is:
interface Result {
foo: number
name: string
bar: {
value: string}}Copy the code
type Chainable<P = {}> = {
option<K extends string, T>
(
key: K extends keyof P ? never : K,
value: T
): Chainable<P & {[key in K]: T}>
get(): P
}
Copy the code
The last element
Implement a generic Last
that takes an array T and returns the type of its Last element.
example:
type arr1 = ['a'.'b'.'c']
type arr2 = [3.2.1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
Copy the code
type Last<T extends any[]> = T extends [...any[], infer Latest] ? Latest : never
Copy the code
Out of the heap
Implement a generic Pop
that takes an array T and returns an array without the last element. example:
type arr1 = ['a'.'b'.'c'.'d']
type arr2 = [3.2.1]
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
Copy the code
type Pop<T extends any[]> = T extends [... infer R, infer L] ? R :never
Copy the code
Promise.all
Implement PromiseAll, which accepts an array of PromiseLike objects and returns a Promise
, where T is the parsed array of results.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string> ((resolve, reject) = > {
setTimeout(resolve, 100.'foo');
});
// expected to be `Promise<[number, number, string]>`
const p = Promise.all([promise1, promise2, promise3] as const)
Copy the code
declare function PromiseAll<T extends readonly unknown[] > (
args: readonly [...T]
) :Promise<{ [P in keyof T]: T[P] extends Promise<infer R> ? R : T[P] }>
Copy the code
See mutable tuples: document PR
Type Lookup
Finds a type in a union based on its attributes. Expect the LookUp < Dog | the Cat, ‘Dog’ > get the Dog, the LookUp < Dog | Cat, ‘Cat’ > get the Cat.
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`
Copy the code
type LookUp<U, T> = U extends { type: T } ? U : never
Copy the code
Trim Left
Delete the opening space example:
type trimed = TrimLeft<' Hello World '> // expected to be 'Hello World '
Copy the code
type TWhiteSpace = ' ' | '\n' | '\t'
type TrimLeft<S extends string> = S extends `${TWhiteSpace}${infer R}`
? TrimLeft<R>
: S
Copy the code
Trim
Remove whitespace from both sides
type trimed = Trim<' Hello World '> // expected to be 'Hello World'
Copy the code
type TWhiteSpace = ' ' | '\n' | '\t'
type TrimLeft<S extends string> = S extends `${TWhiteSpace}${infer R}`
? TrimLeft<R>
: S
type TrimRight<S extends string> = S extends `${infer R}${TWhiteSpace}`
? TrimRight<R>
: S
type Trim<T extends string> = TrimRight<TrimLeft<T>>
Copy the code
See more information about template strings and infer:
Infer: www.typescriptlang.org/docs/handbo…
Support for template strings: github.com/microsoft/T…
Capitalize
Capitalize converts the first letter to Capitalize
example:
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
Copy the code
type Capitalize<S extends string> = S extends `${infer L}${infer R}`
? `${Uppercase<L>}${R}`
: ' '
Copy the code
Replace
example:
type replaced = Replace<'types are fun! '.'fun'.'awesome'> // expected to be 'types are awesome! '
Copy the code
type Replace<
S extends string,
From extends string,
To extends string
> = From extends ' '
? S
: S extends `${infer L}${From}${infer R}`
? `${L}${To}${R}`
: S
Copy the code
ReplaceAll
example:
type replaced = ReplaceAll<'t y p e s'.' '.' '> // expected to be 'types'
Copy the code
type ReplaceAll<
S extends string,
From extends string,
To extends string
> = From extends ' '
? S
: S extends `${infer L}${From}${infer R}`
? `${ReplaceAll<L, From, To>}${To}${ReplaceAll<R, From, To>}`
: S
Copy the code
Additional parameters
example:
type Fn = (a: number, b: string) = > number
type Result = AppendArgument<Fn, boolean>
(a: number, b: string, x: Boolean) => number
Copy the code
type AppendArgument<Fn extends(... s:any[]) = >any, A> = Fn extends (
...s: infer T
) => infer R
? (. s: [...T, A]) = > R
: never
Copy the code
Permutation
Implements the conversion of a union type to an array type containing a union permutation
example:
type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
Copy the code
// Distribute the first element, then remove the first element, recursively determine the remaining elements
type Permutation<T, P = T> = [T] extends [never]? [] : Textends any
? [T, ...Permutation<Exclude<P, T>>]
: never
Copy the code
Length of String
Calculates the length of the string
type LengthOfString<S extends string, T extends any[] = []> = S extends `${infer L}${infer R}`
? LengthOfString<R, [...T, L]>
: T['length']
Copy the code
Flatten
example:
type flatten = Flatten<[1.2[3.4], [[[5]]]] >// [1, 2, 3, 4, 5]
Copy the code
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? [...(First extends any[] ? Flatten<First> : [First]), ...Flatten<Rest>]
: T
Copy the code
Append to object
Implements a type that adds a new field to the interface
example:
type Test = { id: '1' }
type Result = AppendToObject<Test, 'value'.4> // expected to be { id: '1', value: 4 }
Copy the code
type Merge<T> = {
[P in keyof T]: T[P]
}
type AppendToObject<T, U extends string, V> = Merge<
{ [P in keyof T]: T[P] } & { [P in U]: V }
>
Copy the code
Take note of this:
Intersection equality problem
type Equal<T, D> = (<S>() = > S extends T ? 1 : 2) extends <S>() = > S extends D
? 1
: 2
? true
: false
type A = {
name: string
age: number
}
type B = {
name: string
}
type Merge<T> = {
[P in keyof T]: T[P]
}
type IsEqual1 = Equal<A, B & { age: number} >//false
type IsEqual2 = Equal<A, Merge<B & { age: number} > >// true
Copy the code
Absolute
Realize the Absolute. The type that accepts a string, number, or Bigint. The output should be a positive string
example:
type Test = -100;
type Result = Absolute<Test>; // expected to be "100"
Copy the code
type Number = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type Absolute<T extends number | string | bigint> =
`${T}` extends `${infer First}${infer Reset}`
? `${First}` extends `The ${Number}`
? `${First}${Absolute<Reset>}`
: `${Absolute<Reset>}`
: ' '
Copy the code
String to Union
example:
type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"
Copy the code
type StringToUnion<T extends string> = T extends `${infer First}${infer Reset}`
? First | StringToUnion<Reset>
: never
Copy the code
Merge
Combine two types into a new type. In the case of the same key, the second overrides the first
type IntersectionToObj<T> = {
[P in keyof T]:T[P]
}
type Merge<F, S> = IntersectionToObj<Omit<F, keyof S> & S>
Copy the code
CamelCase
example: for-bar-baz
-> forBarBaz
type ConcatDash<S extends string> = ` -${S}`
type CamelCase<S extends string> = S extends `${infer L}-${infer M}${infer R}`
? M extends The '-'
? `${L}-${CamelCase<ConcatDash<R>>}`
: M extends Uppercase<M>
? `${L}-${M}${CamelCase<R>}`
: `${L}${Uppercase<M>}${CamelCase<R>}`
: S
Copy the code
KebabCase
example: FooBarBaz
-> for-bar-baz
type StringToUnion<T extends string> = T extends `${infer First}${infer Reset}`
? First | StringToUnion<Reset>
: never
type UpperCases = StringToUnion<'ABCDEFGH'> | StringToUnion<'IJKLMNOPQRSTUVXYZ'>
type Split<S extends string> = S extends `${infer L}${infer R}`
? L extends UpperCases
? L extends Uppercase<L>
? ` -${Lowercase<L>}${Split<R>}`
: `${L}${Split<R>}`
: `${L}${Split<R>}`
: S
type DelFirst<T extends string, U extends string> = T extends ` -The ${string}`
? U
: U extends ` -${infer R}`
? R
: U
type KebabCase<T extends string> = DelFirst<T, Split<T>>
Copy the code
Diff
Compare the differences between two object types
type Merge<T> = {
[P in keyof T]: T[P]
}
type Diff<O, O1> = Merge<
| {
[P in Exclude<keyof O, keyof O1>]: O[P]
} &
{
[P in Exclude<keyof O1, keyof O>]: O1[P]
}
>
Copy the code
AnyOf
Accepts Array, returning true if any element in Array is true. Otherwise return false example:
type Sample1 = AnyOf<[1."".false, [], {}]>; // expected to be true.
type Sample2 = AnyOf<[0."".false, [], {}]>; // expected to be false.
Copy the code
type FalseUnion = false | ' ' | 0 | Record<string | number.never> | []
type AnyOf<T extends readonly any[]> = T extends [infer First, ...infer Reset]
? First extends FalseUnion
? AnyOf<Reset>
: true
: false` ``
Copy the code
It is important to note here that {} is a relatively large set
type A = { name: string } extends{}?true : false // true
type C = { name: string } extends Record<string | number.never>?true : false // false
Copy the code
IsNever
example:
type A = IsNever<never> // expected to be true
type B = IsNever<undefined> // expected to be false
type C = IsNever<null> // expected to be false
type D = IsNever<[]> // expected to be false
type E = IsNever<number> // expected to be false
Copy the code
type IsNever<T> = [T] extends [never]?true : false
Copy the code
Never is the smallest set, and only never can be assigned to a never document
IsUnion
example:
type case1 = IsUnion<string> // false
type case2 = IsUnion<string|number> // true
type case3 = IsUnion<[string|number] >// false
Copy the code
The first way
type IsUnion<T> = (T extends any ? (arg: T) = > void : never) extends (
arg: infer U
) => void
? [T] extends [U]
? false
: true
: never
Copy the code
Using the inverse property of union type, if it is a union type, the inverse occurs as an intersection type
The second way
type IsUnion<T , U = T> = T extends U?
[U] extends [T]?false:true
:never;
Copy the code
Taking advantage of the property of the distributive, the distributive occurs if T is a union type, where T is one of the subtypes of the union type, and U is T
ReplaceKeys
Implement a type ReplaceKeys that replaces the key in the union type and skips substitution if some type does not have the key. The type has three parameters. example:
type NodeA = {
type: 'A'
name: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type Nodes = NodeA | NodeB | NodeC
type ReplacedNodes = ReplaceKeys<Nodes, 'name' | 'flag', {name: number.flag: string} >// {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.
type ReplacedNotExistKeys = ReplaceKeys<Nodes, 'name', {aa: number} >// {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
Copy the code
type ReplaceKeys<U, T extends string, Y> = {
[K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K]
}
Copy the code
It is important to note here that map types are also distributed when they receive union types
type NodeA = {
type: 'A'
name: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type Nodes = NodeA | NodeB | NodeC
type Map<T> = {
[P in keyof T]: T[P]
}
// Map<NodeA> | Map<NodeB> | Map<NodeC>
type A = Map<Nodes>
Copy the code
Remove Index Signature
Implement RemoveIndexSignature
to exclude index signatures from object types. Example:
type Foo = {
[key: string] :any;
foo(): void;
}
type A = RemoveIndexSignature<Foo> // expected { foo(): void }
Copy the code
// An index signature parameter type be either 'string' or 'number'.
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K
? never
: number extends K
? never
: K]: T[K]
}
Copy the code
Reference: www.typescriptlang.org/docs/handbo…
Percentage parser
example:
type PString1 = ' '
type PString2 = '+ 85%'
type PString3 = '85%'
type PString4 = '85%'
type PString5 = '85'
type R1 = PercentageParser<PString1> // expected ['', '', '']
type R2 = PercentageParser<PString2> // expected ["+", "85", "%"]
type R3 = PercentageParser<PString3> // expected ["-", "85", "%"]
type R4 = PercentageParser<PString4> // expected ["", "85", "%"]
type R5 = PercentageParser<PString5> // expected ["", "85", ""]
Copy the code
type Num = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type Operation = '+' | The '-'
type PercentageParser<T, U extends string[] = [], N = 0> = N extends 0
? T extends `${infer L}${infer R}`
? L extends Operation
? PercentageParser<R, [L], 1>
: PercentageParser<T, [' '].1>
: PercentageParser<T, [' '.' '].2>
: N extends 1
? T extends `${infer L}${infer R}`
? L extends Num
? PercentageParser<R, [U[0].`${U[1] extends string ? U[1] : ' '}${L}`].1>
: PercentageParser<T, [U[0], U[1] extends string ? U[1] : ' '].2>
: PercentageParser<T, [U[0], U[1] extends string ? U[1] : ' '].2>
: N extends 2
? T extends `${infer L}`
? L extends The '%'
? [U[0], U[1].The '%']
: [U[0], U[1].' ']
: [U[0], U[1].The '%']
: never
Copy the code
Drop Char
Removes the specified character from the string.
example:
type Butterfly = DropChar<' b u t t e r f l y ! '.' '> // 'butterfly! '
Copy the code
type EqualType<T, R> = [T] extends [R] ? ([R] extends [T] ? true : false) : false
type DropChar<
S extends string,
C extends string
> = S extends `${infer L}${infer R}`
? `${EqualType<L, C> extends true ? ' ' : L}${DropChar<R, C>}`
: ' '
// Display exceeds recursion limit
// type DropChar<S extends string, C> = S extends `${infer L}${infer R}`
/ /? L extends C
/ /? C extends L
/ /? DropChar
,>
// : `${L}${DropChar
}`
,>
// : `${L}${DropChar
}`
,>
/ / :"
Copy the code
MinusOne
Given a number (always positive) as a type. Returns the number reduced by 1. example:
type Zero = MinusOne<1> / / 0
type FiftyFour = MinusOne<55> / / 54
Copy the code
type Make10Array<T extends any[]> = [
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T
]
type Make1Array<T, L extends any> = = [] []`${L['length']}` extends T
? L
: Make1Array<T, [...L, 1] >type AddEqualArrayLength<
T extends string,
L extends any[] = []
> = T extends `${infer U}${infer R}`
? AddEqualArrayLength<R, [...Make10Array<L>, ...Make1Array<U>]>
: L
type Pop<T extends any[]> = T extends [...infer F, number]? F :never
type MinusOne<T extends number> = Pop<AddEqualArrayLength<`${T}`> > ['length']
Copy the code
PickByType
example:
type OnlyBoolean = PickByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { isReadonly: boolean; isEnable: boolean; }
Copy the code
type EqualType<T, R> = [T] extends [R]
? [R] extends [T]
? true
: false
: false
type PickByType<T, U> = {
[P in keyof T as EqualType<T[P], U> extends true ? P : never]: T[P]
}
Copy the code
StartsWith
example:
type a = StartsWith<'abc'.'ac'> // expected to be false
type b = StartsWith<'abc'.'ab'> // expected to be true
type c = StartsWith<'abc'.'abcd'> // expected to be false
Copy the code
type StartsWith<T extends string, U extends string> = T extends `${U}The ${string}` ? true : false
Copy the code
EndsWith
Implement EndsWith<T, U>, which takes two exact string types and returns whether T ends in U
type EndsWith<T extends string, U extends string> = T extends `The ${string}${U}` ? true : false
Copy the code
PartialByKeys
Specifies that keys in the object type is optional
example:
interface User {
name: string
age: number
address: string
}
type UserPartialName = PartialByKeys<User, 'name'> // { name? :string; age:number; address:string }
Copy the code
type IntersectionToObj<T> = {
[K in keyof T]: T[K]
}
type PartialByKeys<T , K = any> = IntersectionToObj<{
[P in keyof T as P extends K ? P : never]? : T[P] } & { [Pin Exclude<keyof T, K>]: T[P]
}>
Copy the code
RequiredByKeys
Implement a generic RequiredByKeys
that takes two type parameters T and K. K specifies the set of attributes that should be set to the required T. When K is not provided, it should require all attributes as normal Required
.
example:
interfaceUser { name? :stringage? :numberaddress? :string
}
type UserPartialName = RequiredByKeys<User, 'name'> // { name: string; age? : number; address? : string }
Copy the code
type IntersectionToObj<T> = {
[K in keyof T]: T[K]
}
type RequiredByKeys<T , K = any> = IntersectionToObj<{
[P in keyof T as P extends K ? P : never] -? : T[P] } & { [PinExclude<keyof T, K>]? : T[P] }>Copy the code
Mutable
Implement generic Mutable
, which makes all properties in T Mutable (not read-only).
example:
interface Todo {
readonly title: string
readonly description: string
readonly completed: boolean
}
type MutableTodo = Mutable<T> // { title: string; description: string; completed: boolean; }
Copy the code
type Mutable<T> = {
-readonly [K in keyof T]:T[K]
}
Copy the code
difficult
Simple Vue type
Implement a simplified version of vUe-like type support.
By providing the function name SimpleVue (similar to vue.extend or defineComponent), it should correctly infer the this type inside the calculation and method.
In this challenge, we assume that SimpleVue accepts Object with data, computed, and methods fields as its only parameters,
– Data is a simple function that returns an object that exposes the context this, but you cannot access the data itself or other computer values or methods in data.
– Computed is taking the context as the object of this’s function, doing some computation and returning the result. The computed results should be presented to the context as simple return values rather than functions.
-methods are objects of functions and have the same context as this. Methods can access fields exposed by Data, computed, and other methods. Computed differs from methods in the functionality exposed as-is.
SimpleVue’s return value type can be arbitrary.
// The derivation here is magic
type A = Record<string.any>
interface Options<D, C extends A, M> {
data: (this: void) = > D
computed: C & ThisType<D & C>
methods: M & ThisType<M & { [P in keyof C]: ReturnType<C[P]> }>
}
declare function SimpleVue<D.C extends A.M> (options: Options<D, C, M>) :any
Copy the code
// Another intuitive way to write it
type ComputedReturnType<T> = T extends Record<string.() = > any>? { [Pin keyof T]: ReturnType<T[P]>
}
: never
interface Options<D, C, M> {
data: (this: void) = > D
computed: C & ThisType<D>
methods: M & ThisType<M & ComputedReturnType<C>>
}
declare function SimpleVue<D.C.M> (options: Options<D, C, M>) :any
Copy the code
Reference: function generic ThisType
Cory, 1
example:
const add = (a: number, b: number) = > a + b
const three = add(1.2)
const curriedAdd = Currying(add)
const five = curriedAdd(2) (3)
Copy the code
type Curr<A extends any[], R> = A extends [...infer F, infer L]
? Curr<F, (x: L) = > R>
: R
declare function Currying<T> (
fn: T
) :T extends (. argv: infer A) = >infer R ? Curr<A.R> : never
Copy the code
Union to Intersection
example:
type I = Union2Intersection<'foo' | 42 | true> // expected to be 'foo' & 42 & true
Copy the code
type UnionToIntersection<U> = (U extends any ? (x: U) = > void : never) extends (
x: infer R
) => void
? R
: never
Copy the code
Get Required
Implements the advanced Util type GetRequired
, which preserves all the required field Example:
type I = GetRequired<{ foo: number, bar? :string} >// expected to be { foo: number }
Copy the code
type IfEquals<X, Y> = [X] extends [Y] ? ([Y] extends [X] ? true : false) : false
type GetRequired<T> = {
[P in keyof T as IfEquals<
{ [O in P]: T[P] },
{ [O inP]-? : T[P] } >extends true
? P
: never]: T[P]
}
Copy the code
/ / based on
typeF = { name? :string
}
type R = {
name: string
}
[] is added to prevent union types from assigning patterns
type IfEquals<X, Y> = [X] extends [Y] ? ([Y] extends [X] ? true : false) : false
type G = IfEquals<F, R> // false
Copy the code
Get Optional
Implements the high-level util type GetOptional
, which preserves all optional fields
type I = GetOptional<{ foo: number, bar? :string} >// expected to be { bar? : string }
Copy the code
// one
type IfEquals<X, Y> = [X] extends [Y] ? ([Y] extends [X] ? true : false) : false
type GetOptional<T> = {
[P in keyof T as IfEquals<
{ [O in P]: T[P] },
{ [O inP]-? : T[P] } >extends true
? never: P]? : T[P] }Copy the code
// two
type FilterOptionalKey<T, K extends keyof T> = T extends {
[k inK]-? : T[k] } ?never
: K
type GetOptional<T> = {
[K in keyof T as FilterOptionalKey<T, K>]: T[K]
}
Copy the code
The second approach is based on the following ideas
typehasA = { name? :string
age: number
}
type A = {
name: string
age: number
}
type B = {
name: string
}
type C = hasA extends B ? true : false //false
type D = A extends B ? true : false //true
Copy the code
Is a larger collection when the property is optional
typehasA = { name? :string
age: number
}
type A = {
name: string
age: number
}
type C = hasA extends A ? true : false //false
type D = A extends hasA ? true : false //true
Copy the code
Required Keys
Implement the high-level util type RequiredKeys
, which selects all RequiredKeys as a union. example:
type Result = RequiredKeys<{ foo: number; bar? :string} >.// Expected to be "foo"
Copy the code
type RequiredKeys<T> = keyof {
[P in keyof T as T[P] extends Required<T>[P] ? P : never] : T[P]
}
Copy the code
Optional Keys
Implements the high-level util type OptionalKeys
, which merges all OptionalKeys into a union.
// one
type OptionalKeys<T, K = keyof T> = K extends keyof T
? T extends Required<Pick<T, K>>
? never
: K
: never
Copy the code
Using the allocation mode, check whether T’s keys are optional one by one
//two
type DropUndefined<T> = T extends undefined ? never : T
type OptionalKeys<T> = DropUndefined<
{
[P in keyof T]: {} extends Pick<T, P> ? P : never
}[keyof T]
>
Copy the code
Note here that optional attributes have a union of undefined after mapping
type Map<T> = {
[P in keyof T]: T[P]
}
/* type C = { a: number; b? : string | undefined; } * /
type C = MapThe < {a: number; b? :string} >Copy the code
Capitalize Words
Implement CapitalizeWords
, which converts the first letter of each word in the string to uppercase and leaves the rest as is. example:
type capitalized = CapitalizeWords<'hello world, my friends'> // expected to be 'Hello World, My Friends'
Copy the code
type FirstUppercase<T> = T extends `${infer L}${infer R}`
? `${Uppercase<L>}${R}`
: T
type SpiltCode = ' ' | '. ' | ', '
type Recursion<S extends string> = S extends `${infer M}${infer L}${infer R}`
? M extends SpiltCode
? `${M}${Uppercase<L>}${Recursion<R>}`
: `${M}${Recursion<`${L}${R}`>}`
: S
type CapitalizeWords<T extends string> = Recursion<FirstUppercase<T>>
Copy the code
CamelCase
Implement CamelCase
to convert snake_case string to CamelCase.
example:
type camelCase1 = CamelCase<'hello_world_with_types'> // expected to be 'helloWorldWithTypes'
type camelCase2 = CamelCase<'HELLO_WORLD_WITH_TYPES'> // expected to be same as previous one
Copy the code
type CamelCase<S extends string> = S extends `${infer L}_${infer R}${infer I}`
? `${Lowercase<L>}${Uppercase<R>}${CamelCase<I>}`
: Lowercase<S>
Copy the code
C-printf Parser
C has a function called printf. This function allows us to print content with formatting. Like this,
printf("The result is %d.", 42);
Copy the code
This problem requires you to parse the input string and extract format placeholders, such as %d and %f. For example, if the input string is “The result is %d.”, the parse result is a tuple [‘dec’].
This is the mapping
type ControlsMap = {
c: 'char'.s: 'string'.d: 'dec'.o: 'oct'.h: 'hex'.f: 'float'.p: 'pointer',}Copy the code
type ParsePrintFormat<
S extends string,
T extends any[] = []
> = S extends `The ${string}%${infer M}${infer R}`
? M extends keyof ControlsMap
? ParsePrintFormat<R, [...T, ControlsMap[M]]>
: ParsePrintFormat<R, [...T]>
: T
Copy the code
Vue Basic Props
The challenge starts with Simple Vue, and you should complete that first, then modify your code based on it to start the challenge.
type Constructor<T> = T extends(... args:any) => infer R
? R
: T extends new(... args:any) => infer R2
? R2
: any
type PropType<T> = T extends Array<any>? Constructor<T[number]>
: Constructor<T>
type Prop<P> = {
[K in keyof P]: P[K] extends{}?'type' extends keyof P[K]
? PropType<P[K]['type']>
: PropType<P[K]>
: PropType<P[K]>
}
type ComputedReturnType<T> = T extends Record<string.() = > any>? { [Pin keyof T]: ReturnType<T[P]>
}
: never
interface Options<P, D, C, M> {
props: P
data: (this: Prop<P>) = > D
computed: C & ThisType<D>
methods: M & ThisType<M & ComputedReturnType<C> & Prop<P>>
}
declare function VueBasicProps<P.D.C.M> (options: Options<P, D, C, M>) :any
Copy the code
The important thing to note is that when class is used directly, TS is automatically inferred as typeof class
class ClassA {}
function test<T> (s: T) {
return s
}
// const r: typeof ClassA
const r = test(ClassA)
Copy the code
Combine that with this conclusion
/** * defines a class */
class People {
name: number;
age: number;
constructor(){}}// p1 can be assigned normally
const p1: People = new People();
Type "typeof People" lacks the following attributes of type "People" : name, age
const p2: People = People;
Type "People" is missing attribute "prototype", but type" typeof People" needs attribute
const p3: typeof People = new People();
// p4 can be assigned normally
const p4: typeof People = People;
Copy the code
- When a class is treated directly as a type, the constraint for that type is that it must be an instance of the class
- When the
Typeof class
As a type, the constraint satisfies the type of the class
So it’s going to be
T extends new(... args:any) => infer R2
Copy the code
IsAny
Write a utility type IsAny
that accepts input type T. Return true if T is any, false otherwise
// one
type IsAny<T> = unknown extends T
? [T] extends [string]?true
: false
: false
Copy the code
unkonw
Can only be assigned tounkonw
orany
any
Except you can assign tounkonw
You can also assign values other than never
Reference: www.typescriptlang.org/docs/handbo…
// two
ref: https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
type IsAny<T> = 0 extends (1 & T) ? true : false;
Copy the code
Any is the parent and subclass of all types (except never), so when 1 & any is any
Typed Get
Implementation version TS, similar to loDash get functionality in this challenge does not require access to arrays. example:
type Data = {
foo: {
bar: {
value: 'foobar'.count: 6,},included: true,},hello: 'world'
}
type A = Get<Data, 'hello'> // 'world'
type B = Get<Data, 'foo.bar.count'> / / 6
type C = Get<Data, 'foo.bar'> // { value: 'foobar', count: 6 }
Copy the code
type Get<T, K> = K extends `${infer L}.${infer R}`
? L extends keyof T
? Get<T[L], R>
: never
: K extends keyof T
? T[K]
: never
Copy the code
String to Number
Converts string literals to numbers
type Map = {
'0': []
'1': [1]
'2': [...Map['1'].1]
'3': [...Map['2'].1]
'4': [...Map['3'].1]
'5': [...Map['4'].1]
'6': [...Map['5'].1]
'7': [...Map['6'].1]
'8': [...Map['7'].1]
'9': [...Map['8'].1]}type Make10Array<T extends any[]> = [
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T,
...T
]
type ToNumber<
S extends string,
L extends any[] = []
> = S extends `${infer F}${infer R}`
? ToNumber<R, [...Make10Array<L>, ...(F extends keyof Map ? Map[F] : never)]>
: L['length']
Copy the code
Tuple Filter
Implement a type FilterOut<T, F> that filters out the given type F from the tuple T
example:
type Filtered = FilterOut<[1.2.null.3].null> / / [1, 2, 3]
Copy the code
type FilterOut<T extends any[], F, Stash extends any[] = []> = T extends [
infer L,
...infer R
]
? [L] extends [F]
? FilterOut<R, F, Stash>
: FilterOut<R, F, [...Stash, L]>
: Stash
Copy the code
Tuple to Enum Object
example:
Enum<["macOS"."Windows"."Linux"] >// -> { readonly MacOS: "macOS", readonly Windows: "Windows", readonly Linux: "Linux" }
Enum<["macOS"."Windows"."Linux"].true>
// -> { readonly MacOS: 0, readonly Windows: 1, readonly Linux: 2 }
Copy the code
type IndexOf<T, P, S extends any[] = []> = T extends readonly [
infer F,
...infer R
]
? P extends F
? S['length']
: IndexOf<R, P, [...S, 1]>
: S['length']
type Enum<T extends readonly string[], N extends boolean = false> = {
readonly [P in T[number] as Capitalize<P>]: N extends true ? IndexOf<T, P> : P
}
Copy the code
printf
example:
type FormatCase1 = Format<"%sabc"> // FormatCase1 : string => string
type FormatCase2 = Format<"%s%dabc"> // FormatCase2 : string => number => string
type FormatCase3 = Format<"sdabc"> // FormatCase3 : string
type FormatCase4 = Format<"sd%abc"> // FormatCase4 : string
Copy the code
// Can be extended
type MapDict = {
s: string
d: number
}
type Format<T extends string> = T extends `The ${string}%${infer M}${infer R}`
? M extends keyof MapDict
? (x: MapDict[M]) = > Format<R>
: Format<R>
: string
Copy the code
Deep object to unique
Creates a type that takes an object and makes it and all deeply nested objects in it unique, preserving the string and number keys of all objects, as well as the values of all attributes on those keys.
example:
import { Equal } from "@type-challenges/utils"
type Foo = { foo: 2; bar: { 0: 1 }; baz: { 0: 1}}type UniqFoo = DeepObjectToUniq<Foo>
declare let foo: Foo
declare let uniqFoo: UniqFoo
uniqFoo = foo // ok
foo = uniqFoo // ok
type T0 = Equal<UniqFoo, Foo> // false
type T1 = UniqFoo["foo"] / / 2
type T2 = Equal<UniqFoo["bar"], UniqFoo["baz"] >// false
type T3 = UniqFoo["bar"] [0] / / 1
type T4 = Equal<keyof Foo & string, keyof UniqFoo & string> // true
Copy the code
type Merge<T> = {
[P in keyof T]: T[P]
}
type DeepObjectToUniq<O extends object> = {
[P in keyof O]: O[P] extends object? DeepObjectToUniq<Merge<O[P] & { _xxx? : [O, P] }>> : O[P] }Copy the code
Length of String 2
Implement a type LengthOfString to calculate the length of a template string. This type must support strings several hundred characters long. (In general, recursive calculation of string length is limited by the depth of recursive function calls in TS, that is, it supports strings up to 45 characters long.)
example:
type T0 = LengthOfString<"foo"> / / 3
Copy the code
type LengthOfString<S extends string, T extends any[] = []> = S extends ' '
? T['length']
: S extends `${infer L}${infer Q}${infer W}${infer E}${infer Y}${infer U}${infer I}${infer O}${infer P}${infer R}`
? LengthOfString<R, [...T, L, Q, W, E, Y, U, I, O, P]>
: S extends `${infer L}${infer R}`
? LengthOfString<R, [...T, L]>
: T['length']
Copy the code
And the idea here is very simple, let’s do more matches per recursion,
Union to Tuple
Implement a type, UnionToTuple, that converts the union to a tuple.
Because union types are unordered, but tuples are ordered, it is ok to output elements in any order in this challenge
example:
UnionToTuple<1> // [1], and correct
UnionToTuple<'any' | 'a'> // ['any','a'], and correct
Copy the code
It should not be a tuple union
UnionToTuple<'any' | 'a'> // ['a','any'], and correct
Copy the code
Associations can break down, meaning that some types can absorb (or be absorbed by others) and cannot prevent such absorption. See the following example:
Equal<UnionToTuple<any | 'a'>, UnionToTuple<any>> // will always be a true
Equal<UnionToTuple<unknown | 'a'>, UnionToTuple<unknown>> // will always be a true
Equal<UnionToTuple<never | 'a'>, UnionToTuple<'a'>> // will always be a true
Equal<UnionToTuple<'a' | 'a' | 'a'>, UnionToTuple<'a'>> // will always be a true
Copy the code
answers:
// https://github.com/type-challenges/type-challenges/issues/737
type UnionToIntersection<T> = (T extends any ? (x: T) = > any : never) extends (
x: infer U
) => any
? U
: never
// get last Union: LastUnion<1|2> => 2
// ((x: A) => any) & ((x: B) => any) is overloaded function then Conditional types are inferred only from the last overload
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
type LastUnion<T> = UnionToIntersection<
T extends any ? (x: T) = > any : never
> extends (x: infer L) => any
? L
: never
type UnionToTuple<T, Last = LastUnion<T>> = [T] extends [never]? [] : [...UnionToTuple<Exclude<T, Last>>, Last]Copy the code
What’s harder to understand is LastUnion
-
Here we first convert each element of the union type to a function
(x: T) => any | (x: T) => any | (x: T) => any
-
Using the allocation pattern and the contravariance of the function, the union type is converted to the intersection type
(x: T) => any & (x: T) => any & (x: T) => any
-
When conditional inference is used with multi-signature types (such as function overloading), only the last signature is inferred
Reference: www.typescriptlang.org/docs/handbo…
String Join
Create a string concatenation utility example:
const hyphenJoiner = join(The '-')
const result = hyphenJoiner('a'.'b'.'c'); // = 'a-b-c'
/ / or
join(The '#') ('a'.'b'.'c') // = 'a#b#c'
// When we pass an empty delimiter (i.e. ") to concatenate, the strings should be concatenated as is, i.e
join(' ') ('a'.'b'.'c') // = 'abc'
// When only one item is passed, the original item should be returned (without adding any delimiters)
join(The '-') ('a') // = 'a'
Copy the code
type JoinStr<
J extends string,
T extends string[],
S extends string = ' '
> = T extends [infer F, ...infer R]
? F extends string
? R extends string[]? Sextends ' '
? JoinStr<J, R, `${F}`>
: JoinStr<J, R, `${S}${J}${F}`>
: `${S}${J}${F}`
: S
: S
declare function join<J extends string> (
delimiter: J
).T extends string[] > (. parts: T) = >JoinStr<J.T>
Copy the code
Deepick
Implement a type DeepPick that extends the utility type Pick
exapmle:
type obj = {
name: 'hoge'.age: 20.friend: {
name: 'fuga'.age: 30.family: {
name: 'baz'.age: 1}}}type T1 = DeepPick<obj, 'name'> // { name : 'hoge' }
type T2 = DeepPick<obj, 'name' | 'friend.name'> // { name : 'hoge' } & { friend: { name: 'fuga' }}
type T3 = DeepPick<obj, 'name' | 'friend.name' | 'friend.family.name'> // { name : 'hoge' } & { friend: { name: 'fuga' }} & { friend: { family: { name: 'baz' }}}
Copy the code
type UnionToIntersection<T> = (T extends any ? (x: T) = > any : never) extends (
x: infer R
) => any
? R
: never
type GetValue<T, K extends string> = K extends keyof T
? Pick<T, K>
: K extends `${infer L}.${infer R}`
? {
[P in L]: P extends keyof T ? GetValue<T[P], R> : unknown
}
: unknown
type DeepPick<T, K extends string> = UnionToIntersection<GetValue<T, K>>
Copy the code
Pinia
Create a type-level function whose type is similar to that of the Pinia library. You don’t actually need to implement the function, just add the type.
This function only accepts arguments of one object type. This object contains four properties
id
– Just a string (required)state
– A function that returns an object as a store state (required)getters
– An object with calculated values similar to Vue or getter methods for Vuex, details as follows (optional)actions
– An object with methods that can perform side effects and change state, detailed as follows (optional)
Getters
When you define a store like this
const store = defineStore({
/ /... other required fields
getters: {
getSomething() {
return 'xxx'}}})Copy the code
You can use it like this
store.getSomething
Copy the code
It can’t be used like this
store.getSomething() // error
Copy the code
In addition, getters can access state and/or other getters through this, but state is read-only.
Actions
When you define a store like this
const store = defineStore({
/ /... other required fields
actions: {
doSideEffect() {
this.xxx = 'xxx'
return 'ok'}}})Copy the code
You can call it directly
const returnValue = store.doSideEffect()
Copy the code
Actions can return any value or none, and it can accept any number of different types of arguments. Parameter types and return types cannot be lost, which means type checking must be available on the calling side.
You can get and change state through this, and you can get getters through this but it’s read-only
` `
type GetGetters<T> = {
[P in keyof T]: T[P] extends(... arg:any[]) => infer R ? R : never
}
type OptionsType<S, G, A> = {
id: string
state: () = > Readonly<S>
getters: G & ThisType<GetGetters<G> & Readonly<S>>
actions: A & ThisType<A & S>
}
declare function defineStore<S.G.A> (
store: OptionsType<S, G, A>
) :A & Readonly<S> & GetGetters<G>
Copy the code
The vUE challenge is the same, using the inference of function generics
Camelize
Implement Camelize to convert an object from snake_case to camelCase
example:
Camelize<{
some_prop: string.prop: { another_prop: string },
array: [{ snake_case: string }]
}>
// expected to be
/ / {
// someProp: string,
// prop: { anotherProp: string },
// array: [{ snakeCase: string }]
// }
Copy the code
type TransformCamelcase<T> = T extends `${infer L}_${infer R}`
? `${L}${Capitalize<R>}`
: T
type CamelikeTuple<T> = T extends [infer F, ...infer R]
? [Camelize<F>, ...CamelikeTuple<R>]
: []
type CamelikeObject<T> = {
[P in keyof T as TransformCamelcase<P>]: Camelize<T[P]>
}
type Camelize<T> = T extends Array<any>? CamelikeTuple<T> : Textends{}? CamelikeObject<T> : TCopy the code
Drop String
Removes the specified character from the string.
example:
type Butterfly = DropString<'foobar! '.'fb'> // 'ooar! '
Copy the code
type StringToUnion<S extends string> = S extends `${infer F}${infer Rest}`
? F | StringToUnion<Rest>
: never
type DropStringUnion<
S extends string,
R
> = S extends `${infer A}${infer B}${infer Rest}`
? A extends R
? B extends R
? `${DropStringUnion<Rest, R>}`
: `${B}${DropStringUnion<Rest, R>}`
: B extends R
? `${A}${DropStringUnion<Rest, R>}`
: `${A}${B}${DropStringUnion<Rest, R>}`
: S extends `${infer A}${infer Rest}`
? A extends R
? `${DropStringUnion<Rest, R>}`
: `${A}${DropStringUnion<Rest, R>}`
: S
type DropString<S extends string, R extends string> = DropStringUnion<
S,
StringToUnion<R>
>
Copy the code
Split
example:
type result = Split<'Hi! How are you? '.' '> // should be ['Hi!', 'How', 'are', 'you?']
Copy the code
type Split<
S extends string,
SEP extends string
> = S extends `${infer L}${SEP}${infer R}`
? R extends ' '
? [L]
: [L, ...Split<R, SEP>]
: S extends ' '
? SEP extends ' '
? []
: [S]
: S extends `${infer R}`
? [S]
: string[]
Copy the code
hell
Get read-only fields
Implements the generic GetReadonlyKeys
that returns the union of the read-only keys of an object.
example:
interface Todo {
readonly title: string
readonly description: string
completed: boolean
}
type Keys = GetReadonlyKeys<Todo> // expected to be "title" | "description"
Copy the code
type IsEqual<X, Y, A = true, B = false> = (<T>() = > T extends X
? 1
: 2) extends <T>() = > T extends Y ? 1 : 2
? A
: B
type GetReadonlyKeys<T> = {
[P inkeyof T]-? : IsEqual< { [Oin P]: T[P] },
{ -readonly [O in P]: T[P] },
never,
P
>
}[keyof T]
Copy the code
The rest of the hell level of individuals think it involves more algorithms, so they didn’t continue to do it, interested students can play