4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

type-challenges の中級の易し目な問題に挑戦する【68問中52問】

Last updated at Posted at 2022-10-02

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]>
4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?