yunliuyan / type-challenges

typescript-challenges
0 stars 2 forks source link

00027-medium-replacekeys #27

Open yunliuyan opened 1 year ago

yunliuyan commented 1 year ago

ReplaceKeys medium #object-keys

by 贱贱 @lullabyjune

Take the Challenge    한국어

Implement a type ReplaceKeys, that replace keys in union types, if some type has not this key, just skip replacing, A type takes three arguments.

For example:

type NodeA = {
  type: "A"
  name: string
  flag: number
}

type NodeB = {
  type: "B"
  id: number
  flag: number
}

type NodeC = {
  type: "C"
  name: string
  flag: number
}

type Nodes = NodeA | NodeB | NodeC

type ReplacedNodes = ReplaceKeys<
  Nodes,
  "name" | "flag",
  { name: number; flag: string }
> // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.

type ReplacedNotExistKeys = ReplaceKeys<Nodes, "name", { aa: number }> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never

Back Share your Solutions Check out Solutions
yunliuyan commented 1 year ago

思路

遍历node,若对应的key值属于改变值的联合类型里面,则在改变的对象寻找,找不到为never

代码实现

type ReplaceKeys<T, U, I> = {
  [key in keyof T]:  key extends U ? key extends keyof I ? I[key] : never : T[key]
} 

type ReplacedNodes = ReplaceKeys<
  Nodes,
  "name" | "flag",
  { name: number; flag: string }
> // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.

type ReplacedNotExistKeys = ReplaceKeys<Nodes, "name", { aa: number }> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
Janice-Fan commented 1 year ago
  type ReplaceKeys<T, P, U> = 
    {
      [k in keyof T]: k extends P ? k extends keyof U ? U[k] : never : T[k];
    }
  type ReplacedNodes = ReplaceKeys<
    Nodes,
    "name" | "flag",
    { name: number; flag: string }
  > // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.

  type ReplacedNotExistKeys = ReplaceKeys<Nodes, "name", { aa: number }> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
Naparte commented 1 year ago

type ReplaceKeys<T, K extends keyof P, P> = {
    [key in keyof T]: key extends K ? key extends keyof P ? P[key] : never : T[key];
};
Naparte commented 1 year ago

这里貌似需要一个约束,如果第二个参数比第三个参数多一个,会导致前面的类型没有了

比如这种输入 type ReplacedNodes = ReplaceKeys< Nodes, "name" | "flag" | 'type', { name: number; flag: string }

// {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.

type ReplacedNotExistKeys = ReplaceKeys<Nodes, "name", { aa: number }> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never