[TypeScript] infer

Res1: https://www.typescript-training.com/course/making-typescript-stick/08-type-challenges/#returnoff

Res2: https://learntypescript.dev/09/l2-conditional-infer

 

There is an infer keyword that can be used within a condition in a conditional type to put the inferred type into a variable. That inferred variable can then be used within the conditional branches.

 

type ArrayElementType<T> = T extends (infer E)[] ? E : T;

// type of item1 is number
type item1 = ArrayElementType<number[]>

// type of item2 is `{name: string}`
type item2 = ArrayElementType<{name: string}>

 

For item1, T is number[], so (infer E)[] would match number[], therefore E is number type

For item2, T is {name: string}, so (infer E)[] doesn't match (not array of any type), therefore E would be T which is {name: string}

 

Infer function return type example:

type ReturnOf<F> = F extends (...args: any[]) => infer RT ? RT: F
// F extends (...args: any[]) => any: F should be a function type
// F extends (...args: any[]) => infer RT: the function return type is assigned to a variable RT
// infer RT ? RT: F: is RT a truth type? if yes return RT, otherwise return F

// the following test case can be pass
type cases = [
  // simple 1
  Expect<Equal<boolean, ReturnOf<() => boolean>>>,
  // simple 2
  Expect<Equal<123, ReturnOf<() => 123>>>,
  Expect<
    Equal<ComplexObject, ReturnOf<() => ComplexObject>>
  >,
  Expect<
    Equal<
      Promise<boolean>,
      ReturnOf<() => Promise<boolean>>
    >
  >,
  Expect<Equal<() => "foo", ReturnOf<() => () => "foo">>>,
  Expect<
    Equal<"heads" | "tails", ReturnOf<typeof flipCoin>>
  >,
  Expect<
    Equal<
      "rock" | "paper" | "scissors",
      ReturnOf<typeof rockPaperScissors>
    >
  >
]

 

Enforce the `F` to be a function type:

type ReturnOf<F> = F extends (...args: any[]) => infer RT ? RT: F
const person = { name: "Fred" };
// PersonType is {name: string}
type PersonType = ReturnOf<typeof person>

It would be nice to enforce ReturnOf only accept function as type:

type ReturnOf<F extends (...args: any[]) => any> = F extends (...args: any[]) => infer RT ? RT: F

 

Another way to write ReturnOf<F> is:

type ReturnOf<F extends (...args: any[]) => any> = F extends {(...args: any[]): infer RT} ? RT: never
// F extends {(): any}: F should be any callable
// F extends {(...args: any[]): infer RT}: the return type of the callable should be RT
// F extends {(...args: any[]): infer RT} ? RT: never : if the return type is true, then return RT otherwise return never

 

----- Difficult----

/**
 * Tips: 
 * 1. handle from less restricted type
 *    `Expect<Equal<Split<string, "whatever">, string[]>>`
 *    Here Split<string, "whatever">, string is most generic type
 *    let a: string extends 'abc' ? true: false; // false, general -> specific 
 *    let a: 'abc' extends string ? true: false; // true, specific -> general
 *    only when: let a: string extends string ? true: false; // true, general -> general
 *    
 * 2. Then "", empty string is more specific than `string`
 * 3. You can use multi infer
 *    S extends `${infer FIRST}${SEP}${infer REST}` 
 *    For example: Split<['hello world'], " ">
 *       S: hello world
 *       SEP: " "
 *       ${infer FIRST}${SEP}${infer REST}
 *               |.        |.       |
 *            hello.      " "     world
 *       FIRST is hello
 *       SEP is " "
 *       REST is world
 * 4. Calling recurivly: ? [FIRST, ...Split<REST, SEP>]         
 */
type Split<S extends string, SEP extends string> = string extends S 
  ? string[] 
  : S extends ""
    ? SEP extends "" ? []: [""]
    : S extends `${infer FIRST}${SEP}${infer REST}`
      ? [FIRST, ...Split<REST, SEP>]
      :[S];


// Tests
type cases = [
  Expect<
    Equal<
      Split<"Hi! How are you?", "z">,
      ["Hi! How are you?"]
    >
  >,
  Expect<
    Equal<
      Split<"Hi! How are you?", " ">,
      ["Hi!", "How", "are", "you?"]
    >
  >,
  Expect<
    Equal<
      Split<"Hi! How are you?", "">,
      [
        "H",
        "i",
        "!",
        " ",
        "H",
        "o",
        "w",
        " ",
        "a",
        "r",
        "e",
        " ",
        "y",
        "o",
        "u",
        "?"
      ]
    >
  >,
  Expect<Equal<Split<"", "">, []>>,
  Expect<Equal<Split<"", "z">, [""]>>,
  Expect<Equal<Split<string, "whatever">, string[]>>
]

 

posted @ 2022-07-19 16:24  Zhentiw  阅读(190)  评论(0编辑  收藏  举报