【问题标题】:Can you declare a object literal type that allows unknown properties in typescript?你能声明一个允许 typescript 中的未知属性的对象字面量类型吗?
【发布时间】:2017-03-10 17:05:13
【问题描述】:

基本上我想确保一个对象参数包含所有必需的属性,但可以包含它想要的任何其他属性。例如:

function foo(bar: { baz: number }) : number {
    return bar.baz;
}

foo({ baz: 1, other: 2 });

但这会导致:

Object literal may only specify known properties, and 'other' does not exist in type '{ baz: number; }'.

【问题讨论】:

    标签: generics typescript typescript2.0 object-literal


    【解决方案1】:

    是的,你可以。试试这个:

    interface IBaz {
        baz: number;
        [key: string]: any;
    }
    
    function foo(bar: IBaz) : number {
        return bar.baz;
    }
    
    foo({ baz: 1, other: 2 });
    

    【讨论】:

    • 我最初是这么想的,但想要内联...(出于某种原因,我用括号而不是方括号搞砸了 [key: string] 部分)...
    • 您不必使用any,只需确保包含| number(在此示例中),否则您将在baz: number; 上收到TS 错误。或者你可以使用unknown
    【解决方案2】:

    好吧,我讨厌回答自己的问题,但其他答案激发了我的一些想法……这很有效:

    function foo<T extends { baz: number }>(bar: T): void {
        console.log(bar.baz);
    }
    
    foo({baz: 1, other: 2});
    

    【讨论】:

    • 这看起来你已经设置了它,所以函数接受一个类型参数,但你没有给它一个类型参数。你介意解释一下你在这里做什么吗?谢谢。
    • 他让 TS 根据传递给 bar 参数的值自动推断类型参数。
    • 额外属性应该影响返回类型或被跟踪的应用程序,这是最正确的解决方案。
    【解决方案3】:

    如果已知字段来自泛型类型,则允许使用通配符的方法是使用T &amp; {[key: string]: unknown},任何已知字段都必须符合类型的约束,并且允许其他字段(并视为类型unknown

    这是一个示例:

    type WithWildcards<T> = T & { [key: string]: unknown };
    
    function test(foo: WithWildcards<{baz: number}>) {}
    
    test({ baz: 1 }); // works
    test({ baz: 1, other: 4 }); // works
    test({ baz: '', other: 4 }); // fails since baz isn't a number
    

    如果你有一个泛型类型T,你可以允许带有WithWildCards&lt;T&gt;的通配符字段

    请注意,如果对象来自对象文字以外的任何内容,则不会将额外的属性标记为错误,TS 只是告诉您,在文字中输入该属性是无关的。

    Here are some other cases where extra properties are and aren't allowed

    interface Foos{
      a?: string
      b?: string
    }
    type WithWildcards<T> = T & { [key: string]: unknown };
    
    declare function acceptFoos(foo: Foos): void;
    declare function acceptWild(foo: WithWildcards<Foos>):void
    
    acceptFoos(  {a:'', other: ''}) // ERROR since the property is extraneous
    const data = {a:'', other: ''}
    acceptFoos(  data) // allowed since it is compatible, typescript doesn't force you to remove the properties
    const notCompat = {other: ''}
    acceptFoos(notCompat) //ERROR: Type '{ other: string; }' has no properties in common with type 'Foos'
    acceptFoos({a:'', ...notCompat}) // this is also allowed 
    acceptWild(notCompat) // allowed
    

    【讨论】:

      【解决方案4】:

      如果要允许整个对象的未知属性,可以使用Record

      function doStuff(payload: Record<string|number, unknown>): Record<string|number, unknown> {
        return { anyProp: 'anyValue' }
      }
      
      

      【讨论】:

        【解决方案5】:

        这可以通过为函数参数定义类型定义来最容易地完成。下面是一个内联类型定义的例子:

        function foo(bar: { baz: number }) : number {
            return bar.baz;
        }
        
        const obj = { baz: 1, other: 2 };
        
        foo(obj);
        

        【讨论】:

        • 这似乎会导致另一个类似这样的消息:Type ... has no properties in common with type '...'p using typescript 2.3.4
        • 这是如何工作的?如果您将对象内联,则它不起作用。
        • 这是一个内联类型定义,而不是一个内联对象。在TS Playground 中运行良好。
        【解决方案6】:

        使用unknown 类型时,您可以使用narrowing 的概念来检查您希望从您正在经历的值中获得的类型,并根据您的需要操作这些值 例如

        const messages: string []=Object.values(obj).map(val: unknown=>typeof obj==='object'?obj!['message']:obj)
        
        

        【讨论】:

          猜你喜欢
          • 2020-08-10
          • 2019-07-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多