Typescript中的nameof关键字

Bru*_*ell 28 types typescript

正如我所看到的,没有提供原生nameof-keyword 如C#已建成打字稿.但是,出于与C#相同的原因,我希望能够以类型安全的方式引用属性名称.

当使用jQuery插件(Bootstrap-Tagsinput)或需要配置属性名称的其他库时,这在TypeScript中特别有用.

它可能看起来像:

const name: string = nameof(Console.log);
// 'name' is now equal to "log"
Run Code Online (Sandbox Code Playgroud)

nameConsole.log重构和重命名时,赋值也应该改变.

到目前为止,在TypeScript中使用此类功能的最近方法是什么?

Bru*_*ell 41

正如您已经说过的,从版本2.8开始,TypeScript上没有内置功能.但是,有一些方法可以获得相同的结果:

选项1:使用库

ts-nameof是一个提供C#确切功能的库.有了这个你可以做:

nameof(console); // => "console"
nameof(console.log); // => "log"
nameof<MyInterface>(); // => "MyInterface"
nameof<MyNamespace.MyInnerInterface>(); // => "MyInnerInterface"
Run Code Online (Sandbox Code Playgroud)

选项2:定义辅助函数

您可以轻松定义自己nameof添加类型检查,但它不会自动重构,因为您仍需要键入字符串文字:

nameof<Comment>(c => c.user); // => "user"
nameof<Comment>(c => c.user.posts); // => "user.posts"
Run Code Online (Sandbox Code Playgroud)

它将返回传递的属性名称,但在类型上不存在属性名称时将生成编译时错误T.像这样使用它:

const nameof = <T>(name: keyof T) => name;
Run Code Online (Sandbox Code Playgroud)

积分和更多相关信息

使用TypeScript 2.9+更新辅助函数

该类型keyof T现在不仅解析为字符串,而且解析为string | number | symbol(ref).如果您仍想要仅解析字符串,请改用此实现:

interface Person {
    firstName: string;
    lastName: string;
}

const personName1 = nameof<Person>("firstName"); // => "firstName"
const personName2 = nameof<Person>("noName");    // => compile time error
Run Code Online (Sandbox Code Playgroud)

  • 这是添加到列表中的另一项努力 - https://github.com/IRCraziestTaxi/ts-simple-nameof (2认同)
  • 请注意,如果您的对象也被定义为具有索引签名,则这不起作用,因为这会使编译器接受任何键。 (2认同)
  • 第一个建议的软件包不建议再使用它。 (2认同)

Cia*_*tic 13

如果您只需要以字符串形式访问属性,则可以像这样安全地使用 Proxy:

function fields<T>() {
    return new Proxy(
        {},
        {
            get: function (_target, prop, _receiver) {
                return prop;
            },
        }
    ) as {
        [P in keyof T]: P;
    };
};

interface ResourceRow {
    id: number;
    modified_on_disk: Date;
    local_path: string;
    server_path: string;
}

const f = fields<ResourceRow>();

// In this example I show how to embed field names type-safely to a SQL string:
const sql = `
CREATE TABLE IF NOT EXISTS resource (
    ${f.id}               INTEGER   PRIMARY KEY AUTOINCREMENT NOT NULL,
    ${f.modified_on_disk} DATETIME       NOT NULL,
    ${f.local_path}       VARCHAR (2048) NOT NULL UNIQUE,
    ${f.server_path}      VARCHAR (2048) NOT NULL UNIQUE
);
`;
Run Code Online (Sandbox Code Playgroud)


Sal*_*ain 6

我认为我们通常需要更多:在运行时通过编译时验证来获取类属性名称。这是一个非常有用的功能:

export type valueOf<T> = T[keyof T];
export function nameOf<T, V extends T[keyof T]>(f: (x: T) => V): valueOf<{ [K in keyof T]: T[K] extends V ? K : never }>;
export function nameOf(f: (x: any) => any): keyof any {
    var p = new Proxy({}, {
        get: (target, key) => key
    })
    return f(p);
}
Run Code Online (Sandbox Code Playgroud)

用法示例(无字符串!):

if (update.key !== nameOf((_: SomeClass) => _.someProperty)) {
   // ...                               
}
Run Code Online (Sandbox Code Playgroud)


Mas*_*iri 6

推荐:不要使用“ts-nameof”包

我现在建议不要使用这个包或任何其他编译器转换。它很简洁,但它创建的代码不可移植,并且很难切换到新的构建系统。当前用于注入编译器转换的解决方案很糟糕,我无法想象 TS 编译器能够开箱即用地支持这一点。

/* eslint-disable no-redeclare, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any */
export function nameof<TObject>(obj: TObject, key: keyof TObject): string;
export function nameof<TObject>(key: keyof TObject): string;
export function nameof(key1: any, key2?: any): any {
  return key2 ?? key1;
}
/* eslint-enable */
Run Code Online (Sandbox Code Playgroud)