Typescript-从使用索引签名定义的对象中获取显式对象键类型

时间:2019-07-14 06:45:39

标签: typescript

比方说,以下对象是使用指定的类型定义创建的。它必须使用索引签名[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仅应等于fooKeybarKey

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演示的问题。

1 个答案:

答案 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