【问题标题】:Typescript - get explicit object key type from an object defined with an index signatureTypescript - 从使用索引签名定义的对象中获取明确的对象键类型
【发布时间】:2019-11-23 07:07:36
【问题描述】:

假设以下对象是使用指定的类型定义创建的。它必须使用索引签名[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],因此只要someFunction(key) 中的key 等于任何字符串,就不会出现任何类型错误。

如何确保只有myObject 中的实际键可以作为key 参数传入?例如:

someFunction("fooKey") // should pass
someFunction("barKey") // should pass
someFunction("notAValidKey"); // should fail

这里有一个 Playground link 来演示这个问题。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    不要将对象的类型扩大到带有索引签名的接口。一旦你这样做了,就不可能知道你的对象上有什么键。

    如果您仍需要强制执行 myObject 的类型,您可以将其包装在一个身份函数中,该函数会检查其类型而不扩大它。

    createObject() 包装器不是绝对必要的,但它使在实例化时生成错误的性质而不是使用对象时更容易识别错误的性质。如果您删除对createObject() 的调用并在myObject 的定义中引入错字,您仍然会收到错误,但它会在调用someFunction() 时发生。

    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
    

    【讨论】:

    • 如果我不使用带有索引签名的类型进行扩展,那么我对myObject 没有类型安全性。例如,我可以设置barKey: { oof: "something else" }(拼写错误),它不会检测到任何错误。
    • 我看到你编辑了你的答案,但我不确定它如何适合代码示例。
    • 好的,我想我明白你的意思了。不幸的是,我在现实生活中的例子比这复杂得多,这个解决方案不起作用。简单地说,const someFunction 是在我正在创建的一个单独的 npm 包中定义的,myObject 是用户在项目根目录中定义的。因此,我需要直接为myObject 提供类型安全和自动完成/建议,以便用户在尝试错误定义myObject 时收到警告/错误。
    • @GCM 接口定义中的keyof typeof myObject 在这种情况下无论如何都是不可能的。您打算如何定义该签名?
    • @Daniel 再次向您推荐createStyles。使用标识函数来防止类型扩大是一种非常常见的模式,并且使用像 interface Create&lt;T&gt; { &lt;V extends T&gt;(v: V): V } 这样通用的东西,您可以使用它对几乎任何类型执行类型检查。如果有一种方法可以在没有函数的情况下做到这一点,那么像 material-ui 这样的大型库肯定会使用它,但没有。
    猜你喜欢
    • 2017-06-11
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 2019-12-21
    • 2016-01-25
    • 1970-01-01
    • 2022-09-30
    • 2016-12-02
    相关资源
    最近更新 更多