SoFunction
Updated on 2025-04-07

type challenge (middle part) example analysis

introduction

Continuing with the previous article: [easy part]https:///javascript/

After getting familiar with the basic knowledge points, the middle is smoother overall, but there are too many questions, and it is more about checking for missing and filling in the gaps in knowledge points.

middle part

type MyReturnType<T extends (...args: any[]) => any> = 
    T extends (...args: any[]) => infer R ? R : never;
// Implement Omittype Exclude<T, K> = T extends K ? never : T;
type MyOmit<T, K extends keyof T> = {[k in Exclude<keyof T, K>]: T[k]}
// Specify the part readonly, here assuming that MyOmit has been implemented..type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [k in K]: T[k]} & MyOmit<T, K>
type DeepReadonly<T> = {
  readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>;
};
type TupleToUnion<T extends any[]> = T[number];
type Last<T extends any[]> = T extends [...any[], infer U] ? U : never;
type Pop<T extends any[]> = T extends [...infer U, any] ? U : T;
type LookUp<U, T> = U extends {type: T} ? U : never;
// Trim related, first implement Space typetype Space = ' '|'\n'|'\t';
type TrimLeft<S extends string> = S extends `${Space}${infer U}` ? TrimLeft<U> : S;
type Trim<S extends string> = S extends `${Space}${infer U}`|`${infer U}${Space}` ? Trim<U>: S;
type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : (S extends `${infer H}${From}${infer T}` ? `${H}${To}${T}` : S);

DeepReadonly

This content is still not well-known [distribution condition type], so you can refer to another articledocument.

Chinable

type MyAssign<T extends {}, Q extends {}> = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}
type Chainable<T extends {} = {}> = {
  option<P extends string, Q>(key: P extends keyof T ? never : P, value: Q): Chainable<MyAssign<T, Record<P, Q>>>
  get(): T
}

There are two difficulties here:

How to make it report an error when repeated assignment

const result2 = a
    .option('name', 'another name')
    // @ts-expect-error
    .option('name', 'last name')
    .get()
  • GivenameAfter the attribute is assigned, the value is assigned againnameThe attribute will report an error, butts typeCan't use it directlyNoMost people can only use itSupplementMake a judgment
  • Parameter sideoption<P extends string, Q>It is definitely impossible to use conditional statements to make judgments, if written like thisoption<P extends (P extends keyof T ? never : P), Q>, will directly report an error <span style="color: red">Type parameter 'P' has a circular constraint.</span>
  • therefore,Only judge in the function parametersoption<P extends string, Q>(key: P extends keyof T ? never : P, value: Q)
  • Newly added properties need to be implemented similarAssignMethod

    • Can't be used hereMethod implementation, originalThe implementation method is as follows
    • assign<T extends {}, U>(target: T, source: U): T & U;
    • This implementation brings a problem, i.e.const b = (aa, { name: 11 });typeof bThe result is{ name: string;} & { name: number;}, obviously does not meet the needs, so I realize it myself
    • type MyAssign<T extends {}, Q extends {}> = {[k in (keyof T|keyof Q)]: k extends keyof Q ? Q[k] : (k extends keyof T ? T[k]: undefined)}

PromiseAll

// Final answerdeclare function PromiseAll&lt;A extends any[]&gt;(values: readonly [...A]): Promise&lt;{
  [key in keyof A]: Awaited&lt;A[key]&gt;
}&gt;

If you spell this question slowly according to the test cases, it will not be too difficult. The first pieced together is as follows:

type PromiseValue<T> = T extends Promise<infer V> ? PromiseValue<V> : T;
type PromiseValues<T extends any[], R extends any[] = []> = T extends [infer H, ...infer E] ? PromiseValues<E, [...R, PromiseValue<H>]>: ([] extends R ? PromiseValue<T[number]>[] : R);
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<PromiseValues<T>>;

Two problems were encountered here:

  • PromiseAll<T extends any[]>(values: readonly [...T])Why do you need to use readonly here

    • One of the test cases is like thisPromiseAll([1, 2, (3)] as const)
    • Didn't check it myselfas constWhat does it mean? I asked chat-gpt and the answer I gave is hereas constIt is to turn the data into read-only, which is very clear.readonlyofTnoTWe want to subclassvaluesPromoted toreadonlyTypes of

Is it possible to represent an array using an object?

type A = [number, string, {a: number}]
  type B = {
    [k in keyof A]: A[k]
  }
  type C = A extends B ? (B extends A ? true: false): false; // true!!!
  • This way of copying objects can be used on arrays

MyCapitalize

I didn't think much about this question at the beginning, and I wrote the correct answer directly

type MyCapitalize<S extends string> = S extends `${infer H}${infer T}` ? `${Uppercase<H>}${T}` : S;

But I thought of a question back,infer HWhy only the first letter is matched, it is greedy,inferDon't have it?

No relevant article was found online, so I asked chat-gpt, and it told me seriouslyinfer HThe syntax of this format can only match one letter and gives me the following example

type Substring2&lt;S extends string&gt; = S extends `${infer A}${infer _}${infer B}` ? `${A}${B}` : never;
type Test = Substring2&lt;'hello'&gt;; // chat-gptSay the result is'hl',Actually running is'hllo'

Several more examples have been tested, such as

type UpperFirstPart<S extends string> = S extends `${infer H} ${infer T}` ? `${Uppercase<H>} ${T}` : S;
type Test = UpperFirstPart<"hello world">; // HELLO world

DiscoverinferWillThe first matching pattern found is the standard,similarMethod, in this way, understandMyCapitalizeIt's much easier to implement

The above is the detailed content of the analysis of the sample analysis of the type challenge (middle part). For more information about type challenge middle, please pay attention to my other related articles!