比方说,以下对象是使用指定的类型定义创建的。它必须使用索引签名[key: string]
,因为允许对象具有任何键,甚至根本没有键。
interface CreateObject {
[key: string]: {
foo: string
bar: number
}
}
const myObject: CreateObject = {
fooKey: {
foo: "something",
bar: 1
},
barKey: {
foo: "something else",
bar: 2
}
}
现在,假设我要创建一个接受key
参数的函数。 key
的值应等于myObject
内的实际键值,即在上面的示例中,key
仅应等于fooKey
或barKey
interface SomeFunction {
(key: keyof typeof myObject): void
}
const someFunction: SomeFunction = (key) => {
console.log(myObject[key].foo)
}
这不会起作用,因为keyof typeof myObject
等于[key: string]
,因此,只要key
中的someFunction(key)
等于任何字符串,那么它将没有任何类型错误。
如何确保仅将myObject
中的实际键作为key
参数传入?例如:
someFunction("fooKey") // should pass
someFunction("barKey") // should pass
someFunction("notAValidKey"); // should fail
这里是Playground link演示的问题。
答案 0 :(得分:1)
不要使用索引签名将对象的类型扩展到接口。一旦这样做,就不可能知道对象上有哪些键。
如果仍然需要强制使用myObject
的类型,则可以将其包装在一个标识函数中,该函数可以检查其类型而无需扩展它。
createObject()
包装器不是严格必需的,但是使错误的性质更易于识别,在实例化时生成错误,而不是使用对象。如果您删除对createObject()
的调用并将错字引入myObject
的定义中,您仍然会收到错误,但是在对someFunction()
的调用中会出现错误。 / p>
interface CreateObject {
[key: string]: {
foo: string
bar: number
}
}
function createObject<T extends CreateObject> (object: T) {
return object
}
const myObject = createObject({
fooKey: {
foo: "something",
bar: 1
},
barKey: {
foo: "something else",
bar: 2
}
})
interface SomeFunction {
<T extends CreateObject> (object: T, key: keyof T): void
}
const someFunction: SomeFunction = (object, key) => {
console.log(object[key].foo)
}
someFunction(myObject, 'fooKey') // passes
someFunction(myObject, 'barKey') // passes
someFunction(myObject, 'notAValidKey') // fails