强制使用非索引成员的键和值类型

时间:2018-11-09 20:54:37

标签: typescript

我有一个可以接受任何对象文字的函数,只要对象中的所有值都是字符串:

function getFirstLetters(obj: { [key: string]: string }): string[] {
    return Object.keys(obj).map(key => obj[key][0]);
}

这对于任何索引类型都适用,当我尝试使用非索引对象时会出现问题:

interface SomeData {
    user: string;
    loc: string;
}

const someData: SomeData = {
    user: "coolGuy42",
    loc: "New York",
};

function getFirstLetters(obj: { [key: string]: string }): string[] {
    return Object.keys(obj).map(key => obj[key][0]);
}

// Argument of type 'SomeData' is not
// assignable to parameter of type
// '{ [key: string]: string; }'.
// Index signature is missing in type 'SomeData'.
getFirstLetters(someData);

错误很简单-我特别要求该函数根据具有索引签名的obj进行验证,而不是仅根据其值的类型进行验证。

是否可以使我的函数与所有具有统一值类型而没有的对象一起使用,而不要求使用它的人在其界面中包括索引签名? < / p>

1 个答案:

答案 0 :(得分:1)

您可以使函数generic并要求输入参数为任何已知属性都是字符串的类型:

function getFirstLetters<T extends Record<keyof T, string>>(obj: T): string[] {
  return Object.keys(obj).map(key => obj[key][0]); // error
}

但是编译器(正确地)抱怨说,它不知道obj[key]可能是什么。毕竟,T known 键是string值,但TypeScript中的类型不是exact。类型{foo: string}的值可能具有任意数量的额外属性。我们知道它的foo属性是string,但就我们所知,它可能具有bar的{​​{1}}属性。

如果您确定只会将完全相似的类型传递给number,则可以使用type assertion来使编译器确信自己在做安全的事情:

getFirstLetters

它应该像您预期的那样工作:

function getFirstLetters<T extends Record<keyof T, string>>(obj: T): string[] {
  // no error now
  return (Object.keys(obj) as (Array<keyof T>)).map(key => obj[key][0]);
}

它将拒绝具有非getFirstLetters(someData); // no error s值的已知属性的值:

string

但是,再次提醒您,您可以将一些带有未知非字符串属性的东西传递给它,这会在运行时引起问题:

getFirstLetters({a: "a", b: 23}); // error on b, not a string

由您自己决定是否关心这些极端情况以及如何处理这些情况。无论如何,希望能有所帮助。祝你好运!