Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

「重学TS 2.0 」TS 练习题第三十题 #49

Open
semlinker opened this issue Sep 21, 2021 · 9 comments
Open

「重学TS 2.0 」TS 练习题第三十题 #49

semlinker opened this issue Sep 21, 2021 · 9 comments

Comments

@semlinker
Copy link
Owner

完善 Chainable 类型的定义,使得 TS 能成功推断出 result 变量的类型。调用 option 方法之后会不断扩展当前对象的类型,使得调用 get 方法后能获取正确的类型。

declare const config: Chainable

type Chainable = {
  option(key: string, value: any): any
  get(): any
}

const result = config
  .option('age', 7)
  .option('name', 'lolo')
  .option('address', { value: 'XiaMen' })
  .get()

type ResultType = typeof result  
// 期望 ResultType 的类型是:
// {
//   age: number
//   name: string
//   address: {
//     value: string
//   }
// }

请在下面评论你的答案。

@869288142
Copy link

869288142 commented Sep 22, 2021

declare const config: Chainable

type Simplify<T> = {
    [P in keyof T]: T[P]
}

type Chainable<T = {}> = {
   // S extends string can make S is Template Literal Types
    option<V, S extends string>(key: S, value: V): Chainable<T & {
       // use Key Remapping in Mapped Types generate {  S: V } type  https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#key-remapping-in-mapped-types
        [P in keyof {
            S: S,
        } as `${S}`]: V
    }>
    get(): Simplify<T>
}

const result = config
    .option('age', 7)
    .option('name', 'lolo')
    .option('address', { value: 'XiaMen' })
    .get()


type ResultType = typeof result

@xiaoYuanDun
Copy link

个人觉得这道题主要是要发现, config 可以进行链式调用, 这样可以很容易的联想到 js 中的 return this 这种思路, 那么这里 option 的返回值就应该是一个新的 Chainable, 把添加了新属性的类型当做下一个 ChainableT 即可

关于如何动态命名 key, 可以看一下官方介绍

//  给基础类型 T 增加 { K, V } 键值对
type ReudceType<K extends string, V extends any, T = {}> = T & { [key in [K] as `${K}`]: V }

type Chainable<T = {}> = {
  option<K extends string, V extends any>(key: K, value: V): Chainable<ReudceType<K, V, T>> 
  get(): { [K in keyof T]: T[K] }    //  这里也可以直接返回 T, 不过这样并不直观, 所以手动遍历一下
}

//  测试用例
const result = config
  .option('age', 7)
  .option('name', 'lolo')
  .option('address', { value: 'XiaMen' })
  .get()

type ResultType = typeof result  

@ln0y
Copy link

ln0y commented Sep 26, 2021

type Chainable<R = {}> = {
  option<K extends string | number | symbol, V> (key: K, value: V): Chainable<R & Record<K, V>>
  get (): R
}

@zhaoxiongfei
Copy link

declare const config: Chainable

type ITypes = string | number | symbol;
type Chainable<T = {}> = {
  option<K extends ITypes, V extends any>(key: K, value: V): Chainable<T & Record<K, V>>;
  get(): T;
}

const result = config
  .option('age', 7)
  .option('name', 'lolo')
  .option('address', { value: 'XiaMen' })
  .get()

type ResultType = typeof result
const t: ResultType = {
  age: 30,
  name: 'hello',
  address: {
    value: 'Huizhou'
  }
}
console.log(t);
// 期望 ResultType 的类型是:
// {
//   age: number
//   name: string
//   address: {
//     value: string
//   }
// }

思路: 链式操作的思维

@mingzhans
Copy link

declare const config: Chainable<{}>

type Chainable<T extends {}> = {
  option<K extends PropertyKey, V>(key: K, value: V): Chainable<T & {
    [p in K]: V
  }>
  get(): {
    [p in keyof T]: T[p]
  }
}

const result = config
  .option('age', 7)
  .option('name', 'lolo')
  .option('address', { value: 'XiaMen' })
  .option(3, true)
  .get()

type ResultType = typeof result  

@Gengar-666
Copy link

declare const config: Chainable;

type Chainable<T0 = {}> = {
  option<T, U>(key: keyof T, value: U): Chainable<T0 & { [P in keyof T]: U }>;
  get(): T0;
};

const result = config.option("age", 7).option("name", "lolo").option("address", { value: "XiaMen" }).get();

type ResultType = typeof result;
// 期望 ResultType 的类型是:
// {
//   age: number
//   name: string
//   address: {
//     value: string
//   }
// }

@dolphin0618
Copy link

declare const config: Chainable

type Chainable<T = {}> = {
    option<K extends keyof any, V>(key: K, value: V): Chainable<T & {[P in K]: V}>;
    get(): T;
}

const result = config
    .option('age', 7)
    .option('name', 'lolo')
    .option('address', { value: 'XiaMen' })
    .get()

type ResultType = typeof result

@wermdany
Copy link

已经有一样的了🤣

declare const config: Chainable;

type Chainable<T = {}> = {
  option<K extends string, V extends any>(key: K, value: V): Chainable<{ [P in K]: V } & T>;
  get(): T;
};

const result = config.option("age", 7).option("name", "lolo").option("address", { value: "XiaMen" }).get();

type ResultType = typeof result;

@zjxxxxxxxxx
Copy link

zjxxxxxxxxx commented Mar 24, 2022

type Chainable<T = {}> = {
  option<K extends PropertyKey, V = unknown>(
    key: K,
    value: V
  ): Chainable<
    {
      [X in keyof T | K]: X extends K ? V : X extends keyof T ? T[X] : never;
    }
  >;
  get(): T;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants