TypeScript/Type Challenge
https://gyazo.com/38679d4e9bdec24ee7a68a8ab89d47b0
初級
00004-easy-pick : Pick
code:ts
type MyPick<T, K extends keyof T> = { key in K: Tkey }; keyof T は、型 T の屬性名の配列を表す型
K extends keyof T で、型 K が型 T の屬性名でなければ error
一般に T<U extends S> で型 U が型 S の派生型でなければ error (generic constraints)
{ [key in K]: 〜 } で、型 key は配列型 K の要素
T[key] は、型 T の屬性 key の値を表す型 (indexed access type)
00007-easy-readonly : Readonly
code:ts
readonly 修飾子
type T = { readonly key: value } で、型 T の屬性 key は再代入できない
00011-easy-tuple-to-object : Tuple to Object
code:ts
type TupleToObject<T extends readonly string[]> = { [key in Tnumber]: key }; T extends readonly string[] で、型 T が tuple 型であり、object の屬性名にできる string の tuple 型である樣に限定する
T が配列型ならば T[number] で要素の型を作れる
範圍外に access すると undefined に成るからと言って X | undefined には成らない
T が tuple 型なので、T[number] は具體的な文字列達の union 型に成る
00014-easy-first : First of Array
code:ts
type First<T extends unknown[]> = T extends [] ? never : T0; T extends unknown[] で型 T は配列型でなければならない
T[0] は indexed access type で配列型の指定した位置の要素の型
型 never は型檢査 error
00018-easy-tuple-length : Length of Tuple
code:ts
type Length<T extends readonly unknown[]> = T"length"; 配列型には屬性 length が有り、實行時の配列の長さを表す number 型
tuple 型は型檢査時に長さが決まる
00043-easy-exclude : Exclude
code:ts
type MyExclude<T, U> = T extends U ? never : T;
型 MyExclude<A | B, A> は (A extends A ? never : A) | (B extends A ? never : B) に展開される
$ \frac{(T~{\rm extends}~X?Y:Z)\lbrack T/(T_1|T_2)\rbrack}{(T_1~{\rm extends}~X?Y:Z)|(T_2~{\rm extends}~X?Y:Z)}.
00189-easy-awaited : Awaited
code:ts
type MyAwaited<P extends Promise<unknown>> = P extends Promise<infer Result>
? Result extends Promise<infer _>
? MyAwaited<Result>
: Result
: never;
infer T で型 T は束縛されてゐず導出する型である事を示す
00268-easy-if : If
code:ts
type If<C extends boolean, T, F> = C extends true ? T : F;
00533-easy-concat : Concat
code:ts
type Concat<T extends unknown[], U extends unknown[]> = ...T, ...U; 配列型 T を [...T] として新しい配列型を作れる
00898-easy-includes : Includes
code:ts
// type Equal<U, F> = (<S>() => S extends U ? 0 : 1) extends <S>() => S extends F ? 0 : 1
? (<S>() => S extends U ? 0 : 1) extends <S>() => S extends F ? 0 : 1
? true
: Includes<R, U>
: false;
(<S>() => S extends U ? 0 : 1) extends <S>() => S extends F ? 0 : 1 の解說を
code:ts
type Includes<T extends readonly unknown[], U> = {
I in keyof T: (<S>() => S extends TI ? 0 : 1) extends <S>() => S extends U ? 0
: 1
? true
: false;
? false
: true;
03057-easy-push: Push
code:ts
type Push<T extends unknown[], U> = ...T, U; [...T] の練習
03060-easy-unshift : Unshift
code:ts
type Unshift<T extends unknown[], U> = U, ...T; [...T] の練習
03312-easy-parameters : Parameters
code:ts
type MyParameters<T extends (...args: any[]) => unknown> = T extends (
...args: infer P
) => unknown
? P
: never;
中級
00002-medium-return-type : Get Return Type
code:ts
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
00003-medium-omit : Omit
code:ts
{ [Key as 〜]: 〜 } で object 型の Key を rename できる
as での remap で never を返すと { [never]: 〜 } として key を無かった事にできる
00008-medium-readonly-2 : Readonly 2
code:ts
type MyReadonly2<T, K extends keyof T = keyof T> = {
00009-medium-deep-readonly : Deep Readonly
code:ts
type DeepReadonly<T> = {
};
00010-medium-tuple-to-union : Tuple to Union
code:ts
type TupleToUnion<T extends unknown[]> = Tnumber; 00012-medium-chainable-options : Chainable Options
code:ts
type Chainable<T = {}> = {
option<K extends string, V>(
key: K,
value: V
get(): T;
};
00015-medium-last : Last of Array
code:ts
code:ts
00016-medium-pop : Pop
code:ts
? ButLast
: never;
00020-medium-promise-all : Promise.all
code:ts
declare function PromiseAll<T extends unknown[]>(
): Promise<{
}>;
[1, 2, 3] as const は readonly [1, 2, 3] tuple
readonly [...T] で tuple を可變長配列に變へて扱ふ
00062-medium-type-lookup : Type Lookup
code:ts
type LookUp<U, T> = U extends { type: T } ? U : never;
extends の左邊が union 型であれば distributive に作用する
00106-medium-trimleft : Trim Left
code:ts
type TrimLeft<S extends string> = S extends ${" " | "\n" | "\t"}${infer P}
? TrimLeft<P>
: S;
template literal 型
00108-medium-trim : Trim
code:ts
type Trim<S extends string> = S extends
| ${" " | "\n" | "\t"}${infer P}
| ${infer P}${" " | "\n" | "\t"}
? Trim<P>
: S;
00110-medium-capitalize : Capitalize
code:ts
type Char = {
a: "A";
b: "B";
c: "C";
d: "D";
e: "E";
f: "F";
g: "G";
h: "H";
i: "I";
j: "J";
k: "K";
l: "L";
m: "M";
n: "N";
o: "O";
p: "P";
q: "Q";
r: "R";
s: "S";
t: "T";
u: "U";
v: "V";
w: "W";
x: "X";
y: "Y";
z: "Z";
};
type MyCapitalize<S extends string> = S extends ${infer Head}${infer Tail}
? Head extends keyof Char
? ${Char[Head]}${Tail}
: S
: "";
寫像 (map) が欲しければ Object 型 (map) を使へる
"qwe" は "q" と "we" に分解される。"q" は "q" と "" に分解される。"" は分解されない
00116-medium-replace : Replace
code:ts
type Replace<
S extends string,
From extends string,
To extends string
= S extends ${infer Prefix}${From}${infer Postfix}
? ${Prefix}${Postfix} extends S
? S
: ${Prefix}${To}${Postfix}
: S;
00119-medium-replaceall : ReplaceAll
code:ts
type ReplaceAll<
S extends string,
From extends string,
To extends string
= S extends ${infer Prefix}${From}${infer Postfix}
? ${Prefix}${Postfix} extends S
? S
: ${Prefix}${To}${ReplaceAll<Postfix, From, To>}
: S;
template literal 型は前方から一致するので Prefix は From を含まない
191-medium-append-argument : Append Argument
code:ts
type AppendArgument<Fn, A> = Fn extends (...args: infer Args) => infer Return
: never;
00296-medium-permutation : Permutation
code:ts
type Permutation<T, U = T> = T extends never ? []
: T extends U
: never;
順列 (permutation) を切り出す働きをするのは、T extends U の左邊 T が Union 型である時の distributive な動作
U extends T ? never : U は Exclude<U, T>
00298-medium-length-of-string : Length of String
code:ts
type LengthOfString<
S extends string,
Chars extends string[] = []
= S extends ${infer Head}${infer Tail}
00459-medium-flatten : Flatten
code:ts
type Flatten<T> = T extends unknown[]
: []
code:ts
type Flatten<T> = T extends []
? []
00527-medium-append-to-object : Append to object
code:ts
type AppendToObject<T, U extends keyof any, V> = {
};
keyof any で PropertyKey 型
00529-medium-absolute : Absolute
code:ts
type Absolute<T extends number | string | bigint> = ${T} extends -${infer N}
? N
: ${T};
00531-medium-string-to-union : String to Union
code:ts
type StringToUnion<T extends string> = T extends ${infer Head}${infer Tail}
? Head | StringToUnion<Tail>
: never;
00599-medium-merge : Merge
code:ts
type Merge<F, S> = {
: Key extends keyof F
: never;
};
code:ts
type Merge<F, S> = {
: Key extends keyof F
: never;
};
00612-medium-kebabcase : KebabCase
code:ts
type Char = {
A: "a";
B: "b";
C: "c";
D: "d";
E: "e";
F: "f";
G: "g";
H: "h";
I: "i";
J: "j";
K: "k";
L: "l";
M: "m";
N: "n";
O: "o";
P: "p";
Q: "q";
R: "r";
S: "s";
T: "t";
U: "u";
V: "v";
W: "w";
X: "x";
Y: "y";
Z: "z";
};
type KebabCase<
S,
IsFirst extends boolean = true
= S extends ${infer Head}${infer Tail}
? `${Head extends keyof Char
? ${IsFirst extends true ? "" : "-"}${Char[Head]}
: Head}${KebabCase<Tail, false>}`
: "";
code:ts
type Char = {
// Same as above
};
type KebabCase<S> =
S extends ${infer First}${infer Tail extends ${keyof Char}${infer _}}
? ${First extends keyof Char ? Char[First] : First}-${KebabCase<Tail>}
: S extends ${infer First}${infer Tail}
? ${First extends keyof Char ? Char[First] : First}${KebabCase<Tail>}
: "";
00645-medium-diff : Diff
code:ts
type Diff<O, O1> = {
[Key in keyof (O & O1) as Key extends keyof O1
? Key extends keyof O
? never
: Key
: Key]: Key extends keyof O
: Key extends keyof O1
: never;
};
00949-medium-anyof : AnyOf
code:ts
? Head extends 0 | "" | false | [] | { Key: string: never } ? AnyOf<Tail>
: true
: false;
01042-medium-isnever : IsNever
code:ts
type IsNever<T> = T extends never ? true : false; 01097-medium-isunion : IsUnion
code:ts
type IsUnion<T, U = T> = T extends never ? false
: T extends any
? false
: true
: never;
T extends any で以後の union 型 T を distributive に扱ふ。extends の左邊に現はれない union 型 U は distributive にされないので、すると [a | b] extends [a] は false になり union 型だと判定できる
01130-medium-replacekeys : ReplaceKeys
code:ts
type ReplaceKeys<U, T, Y> = {
? Key extends keyof Y
: never
};
01367-medium-remove-index-signature : Remove Index Signature
code:ts
type RemoveIndexSignature<T> = {
[Key in keyof T as number extends Key
? never
: string extends Key
? never
: symbol extends Key
? never
};
01978-medium-percentage-parser : Percentage Parser
02070-medium-drop-char : Drop Char
02257-medium-minusone : MinusOne
02595-medium-pickbytype : PickByType
02688-medium-startswith : StartsWith
02693-medium-endswith : EndsWith
02757-medium-partialbykeys : PartialByKeys
02759-medium-requiredbykeys : RequiredByKeys
02793-medium-mutable : Mutable
上級
00006-hard-simple-vue : Simple Vue
最上級
00005-extreme-readonly-keys : Get Readonly Keys