网站搜索

在 TypeScript 中是否可以指定字符串应该匹配接口属性名?


例如,我有一个这样的接口:

interface Account {
    email: string;
    enabled: boolean;
}

我想创建一个方法,为这些字段返回默认值。所以我有这样的东西:

function defaultValue(propName:string) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
}

它运行良好,然而如果 Account 的任何属性发生变化,defautValue() 方法仍然可以编译,即使它现在已经损坏。

所以我想知道我是否可以指定 propName 应该具有像 Account 的属性名 这样的类型?或者是否有其他好的模式可以在这种情况下强制类型检查?

答案#1

你可以使用一个函数来断言变量在 if 语句结束时永远不会,并使用 keyof T 来获取键的联合类型:

function assertNever(v: never): never {
  throw new Error("Should never happen!");
}

function defaultValue(propName:keyof Account) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
    assertNever(propName);
}

游乐场链接

你也可以通过使函数通用来改善调用者的体验,但这将意味着在实现中需要进行一些类型断言。

function defaultValue<K extends keyof Account>(propName:K): Account[K] {
    if (propName === 'email') return '' as Account[K];
    if (propName === 'enabled') return true as Account[K];
    assertNever(propName);
}

let email = defaultValue('email') // string
let enabled = defaultValue('enabled') // boolean

游乐场链接

答案#2

你可以通过给 propName 参数添加一个更具体的字符串类型来实现这一点。然后,使用 switch 语句来进行智能类型推断,以确保你在函数的每个可能分支中正确返回与返回类型匹配的值。

interface Account {
    email: string;
    enabled: boolean;
    shouldError: boolean;
}

function defaultValue(propName: keyof Account): Account[keyof Account] { // shows error for not always returning value
    switch (propName) {
        case "email":
            return "";
        case "enabled":
            return true;
        // uncomment this code to fix the error
        // case "shouldError":
        //     return true;
    }
}

TypeScript 游乐场链接