type challenge はこちら
初級編と基本的なテクニックはこちら
初級との違い(個人の感想です)
-
再起処理が必要なケースがある
-
カウントを取るテクニックを利用する
こんな感じ.ts
// 再帰処理をする場合に Hoge<T, [...Counter, 0]> とすることで、再帰処理した回数をカウントできる
Hoge<T, Counter extends 0[] = []> = ...
- いくつかは初級と大差ない問題がある
以下解答
2 - Get Return Type.ts
// (...arg: unknown[])とすると 戻り値がユニオン型の関数の戻り値が取れない
type MyReturnType<T extends Function> = T extends (...arg: any[]) => infer R ? R : never
3 - Omit.ts
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
8 - Readonly 2.ts
// K がない場合の挙動が K = keyof T である点に注意
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P]
} & {
[P in keyof T as P extends K ? never: P]: T[P]
}
9 - Deep Readonly.ts
type NonNestedObject = string | number | boolean | Function
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends NonNestedObject ? T[P] : DeepReadonly<T[P]>
}
10 - Tuple to Union.ts
type TupleToUnion<T extends unknown[]> = T[number]
15 - Last of Array.ts
type Last<T extends any[]> = T extends [...infer L, infer R] ? R : never
16 - Pop.ts
type Pop<T extends any[]> = T extends [...infer L, infer R] ? L : never
20 - Promise.all.ts
declare function PromiseAll<T extends any[]>(values: readonly [...T]):
//には以下の両方入るが、
declare function PromiseAll<T extends any[]>(values: [...T]):
//には as const がついていない方がしか入らない。
PromiseAll([1, 2, Promise.resolve(3)] as const)
PromiseAll([1, 2, Promise.resolve(3)])
// T = [1,2,3] に対し、
// { [key in keyof T] : T[key] } とすると、プロパティは必ず0,1,2が入り、 T[key]の配列型になる
// 答え
declare function PromiseAll<T extends any[]>(values: readonly [...T]):
Promise<{
[key in keyof T] : T[key] extends Promise<any> ? Awaited<T[key]> : T[key]
}>
62 - Type Lookup.ts
type HasType = { type: string }
type LookUp<U extends HasType, T extends string> = U extends { type: T } ? U : never
106 - Trim Left.ts
type TrimTarget = ' ' | "\n" | "\t"
type TrimLeft<S extends string> = S extends `${TrimTarget}${infer A}`
? TrimLeft<A>
: S
108 - Trim.ts
type TrimTarget = ' ' | "\n" | "\t"
type Trim<S extends string> = S extends `${TrimTarget}${infer A}` | `${infer A}${TrimTarget}`
? Trim<A>
: S
110 - Capitalize.ts
type MyCapitalize<S extends string> = S extends `${infer A}${infer Rest}`
? `${Uppercase<A>}${Rest}`
: S
116 - Replace.ts
type Replace<S extends string, From extends string, To extends string> = S extends `${infer L}${From}${infer R}`
? From extends ''
? S
: `${L}${To}${R}`
: S
119 - ReplaceAll.ts
type ReplaceAll<S extends string, From extends string, To extends string> = S extends `${infer L}${From}${infer R}`
? From extends ''
? S
: `${L}${To}${ReplaceAll<R , From, To>}`
: S
191 - Append Argument.ts
type AppendArgument<Fn, A> = Fn extends (...args: infer Args) => infer R
? (...args: [...Args, A]) => R
: never
298 - Length of String.ts
// S が '' になるまで再帰処理をする
// すると Temp は S の文字列の数と同じだけの要素をもつ配列になる
type LengthOfString<S extends string, Temp extends string[] = []> = S extends `${infer L}${infer R}`
? LengthOfString<R, [...Temp, L]>
: Temp['length']
459 - Flatten.ts
type Flatten<T extends unknown[]> = T extends [infer L, ...infer R]
? L extends unknown[]
? Flatten<[...L, ...R]>
: [L, ...Flatten<R>]
: T
527 - Append to object.ts
type AppendToObject<T, U extends PropertyKey, V> = {
[P in keyof T|U]: P extends keyof T ? T[P] : V
}
529 - Absolute.ts
// 一旦文字列に変換することで `-${infer A}` でstringもnumberも同じように扱うことができるようになる
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer A}`
? `${A}`
: `${T}`
531 - String to Union.ts
type StrToArray<S extends string, A extends unknown[] = []> = S extends `${infer L}${infer R}`
? [L, ...StrToArray<R, [L]>]
: []
type StringToUnion<T extends string> = StrToArray<T>[number]
599 - Merge.ts
type Merge<F, S> = {
[P in keyof F| keyof S]: P extends keyof S
? S[P]
: P extends keyof F
? F[P]
: never
}
612 - KebabCase.ts
type UpperCaseAlphabet = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' // 他にも追加する
type KebabCase<S extends string> = S extends `${infer L}${infer R}`
? L extends UpperCaseAlphabet
? KebabCase<`${Lowercase<L>}${R}`> // 最初が大文字である場合のみの対応
: R extends `${infer RL}${infer RR}`
? RL extends UpperCaseAlphabet
? `${L}-${KebabCase<`${Lowercase<RL>}${RR}`>}` // 二文字目が大文字である場合
: `${L}${KebabCase<R>}`
: S
: S /
645 - Diff.ts
// もっとスマートなやり方あるかも
type Diff<O, O1> = {
[P in keyof O| keyof O1 as P extends keyof O & keyof O1 ? never : P]: P extends keyof O
? O[P]
: P extends keyof O1
? O1[P]
: never
}
949 - AnyOf.ts
// {} を表現するには Record<string, never> を使う
type Falsy = 0 | '' | false | [] | Record<string, never>
type AnyOf<T extends readonly any[]> = T extends Falsy[] ? false : true
1042 - IsNever.ts
type IsNever<T> = [T] extends [never] ? true : false
// 以下の場合、 右辺の extends の評価対象の T に never が入った瞬間、 never を返してしまう
// [] に入れてやり、ワンクッション置くことで評価ができるようになる。
type IsNever<T> = T extends never ? true : false
type a = IsNever<never> //=> never
1130 - ReplaceKeys.ts
type ReplaceKeys<U, T, Y> = {
[P in keyof U]: P extends T
? P extends keyof Y
? Y[P]
: never
: U[P]
}
1367 - Remove Index Signature.ts
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K
? never
: number extends K
? never
: symbol extends K
? never : K
]: T[K]
}
1978 - Percentage Parser.ts
type PlusOrMinus = '+' | '-'
type PercentageParser<A extends string> = A extends `${infer L extends PlusOrMinus}${infer R}`
? R extends `${infer C}%`
? [L, C, '%']
: [L, R, '' ]
: A extends `${infer L}%`
? ['', L, '%' ]
: ['', A, '']
2070 - Drop Char.ts
// 以下をクリアせずに回答している人が多いイメージでした
// Expect<Equal<DropChar<'butter fly!', ''>, 'butterfly!'>>,
type DropChar<S, C> = C extends ''
? DropChar<S, ' '>
: S extends `${infer L}${infer R}`
? L extends C
? DropChar<R, C>
: `${L}${DropChar<R, C>}`
: S
2595 - PickByType.ts
type PickByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never ]: T[P]
}
2688 - StartsWith.ts
type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false
2693 - EndsWith.ts
type EndsWith<T extends string, U extends string> = T extends `${infer R}${U}` ? true : false
2759 - RequiredByKeys.ts
// ↓この記法を知っているかどうかが肝
// [P in T]-?: T[P]
type Merge<T> = {
[P in keyof T]: T[P]
}
type RequiredByKeys<T, K = any> = Merge<{
[P in keyof T as P extends K ? P : never]-?: T[P]
} & {
[P in keyof T as P extends K ? never : P]?: T[P]
}>
2793 - Mutable.ts
type Mutable<T extends object> = {
-readonly [P in keyof T]: T[P] // この記法を知っているかが肝
}
2852 - OmitByType.ts
type OmitByType<T, U> = {
[P in keyof T as T[P] extends U ? never : P]: T[P]
}
3062 - Shift.ts
type Shift<T extends unknown[]> = T extends [infer L, ...infer R] ? R : []
3192 - Reverse.ts
type Reverse<T extends unknown[]> = T extends [infer L, ...infer R]
? [...Reverse<R>, L]
: []
3196 - Flip Arguments.ts
type Reverse<T extends unknown[]> = T extends [infer L, ...infer R]
? [...Reverse<R>, L]
: []
type FlipArguments<T extends (...args: any) => any > = T extends (...args: infer A) => infer B
? (...args: Reverse<A>) => B
: never
3326 - BEM style string.ts
type BEM<B extends string, E extends string[], M extends string[]> = E extends []
? M extends []
? B
: `${B}--${M[number]}`
: M extends []
? `${B}__${E[number]}`
: `${B}__${E[number]}--${M[number]}`
4179 - Flip.ts
// Type 'T[P]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
// と出たので、そのままそれを使った
type Stringifiable = string | number | bigint | boolean | null | undefined
type Flip<T extends object> = {
[P in keyof T as T[P] extends Stringifiable ? `${T[P]}` : never]: P
}
4425 - Greater Than.ts
type GreaterThan<T extends number, U extends number, A extends 0[] = []> = U extends A['length']
? T extends A['length']
? false
: true
: T extends A['length']
? false
: GreaterThan<T, U, [...A, 0]>
4484 - IsTuple.ts
type IsTuple<T> = [T] extends [never]
? false
: T extends readonly [any?]
? true
: false
4499 - Chunk.ts
// まず 結果全体の配列と、 N個の要素でまとまった配列を作ることを考えた
type Chunk<T extends unknown[], N extends number, Memory extends unknown[] = [], Short extends unknown[] = []> = T extends [infer L, ...infer R]
? [0, ...Short]['length'] extends N // Short['length'] extends N だと想定より一回多くループが走るので 0 を足して強引にサイズを合わせた
? Chunk<R, N, [...Memory, [...Short, L]], []>
: Chunk<R, N, Memory, [...Short, L]>
: Short['length'] extends 0
? Memory
: [...Memory, Short]
4518 - Fill.ts
// すごい力技...笑
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T['length'],
Count extends 0[] = [],
Flag extends boolean = false
> = T extends [infer L, ...infer R]
? Start extends End
? T
: Count['length'] extends Start
? [N, ...Fill<R,N,Start,End,[...Count, 0], true>]
: Count['length'] extends End
? [L, ...Fill<R,N,Start,End,[...Count, 0], false>]
: Flag extends true
? [N, ...Fill<R,N,Start,End,[...Count, 0], Flag>]
: [L, ...Fill<R,N,Start,End,[...Count, 0], Flag>]
: []
4803 - Trim Right.ts
type TrimTarget = ' ' | "\n" | "\t"
type TrimRight<S extends string> = S extends `${infer A}${TrimTarget}`
? TrimRight<A>
: S
5117 - Without.ts
type Without<T, U extends number[]|number> = T extends [infer L, ...infer R]
? U extends number[]
? L extends U[number]
? [...Without<R, U>]
: [L, ...Without<R, U>]
: L extends U
? [...Without<R, U>]
: [L, ...Without<R, U>]
: []
5140 - Trunc.ts
type Trunc<T extends string | number> = `${T}` extends `${infer L}.${infer R}`
? L
: `${T}`
5153 - IndexOf.ts
type IndexOf<T, U, Count extends 0[] = []> = T extends [infer L, ...infer R]
? Equal<L, U> extends true
? Count['length']
: IndexOf<R, U, [...Count, 0]>
: -1
5310 - Join.ts
// ...infer R extends string[] としないと、 Rにstring以外の可能性が残るので、 Join<R, U> にR をアサインできない
type Join<T extends string[], U extends string | number> = T extends [infer L, ...infer R extends string[]]
? L extends string
? R extends []
? L
: `${L}${U}${Join<R, U>}`
: ''
: never
5317 - LastIndexOf.ts
type LastIndexOf<T, U, Count extends 0[] = [], Answer extends number = -1> = T extends [infer L, ...infer R]
? Equal<L, U> extends true
? LastIndexOf<R, U, [...Count, 0], Count['length']>
: LastIndexOf<R, U, [...Count, 0], Answer>
: Answer
5360 - Unique.ts
type Contain<T extends unknown[], S extends unknown> = T extends [infer L, ...infer R]
? Equal<L, S> extends true
? true
: Contain<R, S>
: false
type Unique<T extends unknown[], Answer extends unknown[] = []> = T extends [infer L, ...infer R]
? Contain<Answer, L> extends true
? Unique<R, Answer>
: Unique<R, [...Answer, L]>
: Answer
7544 - Construct Tuple.ts
type ConstructTuple<L extends number, M extends unknown[] = []> = M['length'] extends L
? M
: ConstructTuple<L, [...M, unknown]>