我有一个可以接受任何对象文字的函数,只要对象中的所有值都是字符串:
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>
答案 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
由您自己决定是否关心这些极端情况以及如何处理这些情况。无论如何,希望能有所帮助。祝你好运!