首页 > 解决方案 > 如何在 Typescript 定义中表示对象键替换?

问题描述

目前我有以下内容types

type PossibleKeys = number | string | symbol;
type ValueOf<T extends object> = T[keyof T]; 
type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>>> = 
  Omit<T, keyof U> & { [P in ValueOf<U>]: T[keyof U] };

...但是,尽管它甚至可以部分工作,但它给出了以下错误:

类型 'U[keyof U]' 不能分配给类型 'string | 号码 | 象征'。


一个简单的演示

interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

type ReplacedUser = ReplaceKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'items' }>;

ReplacedUser我可以看到类型几乎是正确的。推断类型为:

{ id: string; total: number | readonly Item[]; items: number | readonly Item[]; }

...虽然我期待:

{ id: string; total: number; items: readonly Item[]; }

我究竟做错了什么?我想首先知道如何表达P需要传入的U以抑制 Typescript 错误,然后为特定的value.

标签: typescripttypescript-generics

解决方案


最简单的方法是反转U您的类型参数ReplaceKeys

type PossibleKeys = number | string | symbol;
type ReplaceKeys<T extends {}, U extends Record<PossibleKeys, keyof T>> = Omit<T, ValueOf<U>> & {
    [K in keyof U]: T[U[K]]
};

然后您可以像这样使用它:

type ReplacedUser = ReplaceKeys<MyInterface, { total: 'propToReplace', items: 'anotherPropToReplace' }>;

如果你不能改变U事物的形状变得有点棘手:

// Example types from your post
interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

// All possible key types
type PossibleKeys = number | string | symbol;

// Helper type to get all non-nullable values from type T
type DefinedValues<T> = NonNullable<T[keyof T]>;

// Helper type for your replacements object - a record whose values are valid keys
// and whose keys are also present in type T (the input type)
// 
// Partial is used to make sure you don't need to pass all keys of T in your replacements
type Replacements<T extends {}> = Partial<Record<keyof T, PossibleKeys>>;

// Helper type that swaps object keys for values
type Invert<T extends Replacements<C>, C extends {} = {}> = {
  [N in DefinedValues<T>]: {
    [K in keyof T]: N extends T[K] ? K : never
  }[keyof T]
}

type ReplacedKeys<T extends {}, R extends Replacements<T>> = Omit<T, keyof R | DefinedValues<R>> & {
  [N in keyof Invert<R>]: {
    [L in keyof R]: N extends R[L] ? (L extends keyof T ? T[L] : never) : never;
  }[keyof R]
}

请注意,使用第二种方法不会警告您有重复的映射:

type ReplacedUser = ReplacedKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'total' }>;

检查这里的操场


推荐阅读