如何使用 Typescript 选择和重命名某些键?

yah*_*rga 7 typescript

我有一个界面User

interface User {
    _id     : string;
    name    : string;
    email   : string;
    password: string;
    phone   : number;
}
Run Code Online (Sandbox Code Playgroud)

我有另一个界面UpdatedBy

interface UpdatedUser {
    id  : string;
    name: string;
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用Pick,但我想在界面中重命名_id为。idUpdatedUser

type UpdatedUser = Pick<User, '_id' | 'name'>; // How can I turn _id into id?
Run Code Online (Sandbox Code Playgroud)

更新:我基本上想做一个更干净的版本:

export interface UpdatedUser extends Pick<User, 'name'> {
    id  : Extract<User, '_id'>;
}
Run Code Online (Sandbox Code Playgroud)

for*_*d04 20

重命名没有内置类型Pick,幸运的是我们可以通过合理的努力创建一个。

简单变体

type IdRenamed = Omit<User, "_id"> & { id: User["_id"] }
// { name: string; email: string; password: string; phone: number; id: string;}
Run Code Online (Sandbox Code Playgroud)

操场

单个属性的动态版本

type PickRename<T, K extends keyof T, R extends PropertyKey> =
    Omit<T, K> & { [P in R]: T[K] }

type T21 = PickRename<User, "_id", "id"> // same type as above
type T22 = PickRename<User, "foo", "id"> // error, foo is no property
Run Code Online (Sandbox Code Playgroud)

操场

TS 4.1替代方案:使用映射类型as子句。它的优点是保留了属性的readonlyor 可选 ( ?) 修饰符(有关更多详细信息,请参阅同态映射类型12)。

type PickRename<T, K extends keyof T, R extends PropertyKey> = {
    [P in keyof T as P extends K ? R : P]: T[P]
} // type instantiation same as previous example
Run Code Online (Sandbox Code Playgroud)

操场

多个属性的动态版本

type PickRenameMulti<T, R extends
    { [K in keyof R]: K extends keyof T ? PropertyKey : "Error: key not in T" }
    > = Omit<T, keyof R> & UnionToIntersection<
        { [P in keyof R & keyof T]: { [PP in R[P]]: T[P] } }[keyof R & keyof T]
    >

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends
    ((k: infer I) => void) ? I : never

type T31 = PickRenameMulti<User, { _id: "id"; name: "firstName" }>
type T32 = PickRenameMulti<User, { foo: "id" }> // error, foo is no property
Run Code Online (Sandbox Code Playgroud)

注意:有关UnionToIntersectionhelper 的更多详细信息,请参阅 great type。

操场

TS 4.1 再次简化了语法并生成了同态映射类型:
type PickRenameMulti<T, R extends
    { [K in keyof R]: K extends keyof T ? PropertyKey : "Error: key not in T" }
    > = { [P in keyof T as P extends keyof R ? R[P] : P]: T[P] }
Run Code Online (Sandbox Code Playgroud)

操场

TS 4.1:_从所有属性键中删除前缀

type DropUnderscore<T> = {
    [K in keyof T as K extends `_${infer I }` ? I : K]: T[K]
};
type T4 = DropUnderscore<User> // "_id" and "_email" renamed to "id", "email"
Run Code Online (Sandbox Code Playgroud)

操场

  • 我几乎要放置非常相似的实用程序类型。嗯,你跑得很快。不错的工作! (2认同)

Mar*_*tin 8

稍微干净一点的版本是......

export interface UpdatedUser extends Pick<User, 'name'> {
  id: User['_id'];
}
Run Code Online (Sandbox Code Playgroud)

...但我不确定如何按照您的建议即时重命名它。这是一个有趣的用例。