【问题标题】:Typescript: object with at least one property of type TTypescript:具有至少一个 T 类型属性的对象
【发布时间】:2019-04-03 00:48:47
【问题描述】:

如果HasNumberAndString 上的名称不是number,我怎么能写HasNumber 而没有打字稿给我一个错误?我想要一种方法来强制 HasNumberAndString 具有至少一个 number 类型的属性(不强制属性名称),但也允许拥有其他类型的属性。这可能吗?

interface HasNumber {
    [key: string]: number;
}

interface HasNumberAndString extends HasNumber {
    age: number;
    name: string;
}

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    TypeScript 中没有很好的方法来允许具有索引签名的类型具有与索引签名类型不匹配的附加属性。请参阅microsoft/TypeScript#17867 以获取允许索引签名例外的建议,并可能给它一个 ? 以表示对它的支持。不过,目前还没有直接支持。


    一种解决方法是使用type intersection 而不是扩展接口:

    type SortaHasNumberAndString = { [key: string]: number } & { age: number, name: string };
    

    您基本上是在试图愚弄编译器,因为从技术上讲,这意味着name 属性应该是stringnumber 类型,但您希望编译器只注意到string 部分。这在从该类型的值中读取属性时确实有效:

    declare const sortaHasNumberAndString: SortaHasNumberAndString;
    sortaHasNumberAndString.name; // string, not number
    sortaHasNumberAndString.age; // number
    sortaHasNumberAndString.numberOfLimbs; // number due to index signature
    

    但在尝试分配给该类型的值时效果不佳:

    // error, complains that name is not string & number
    const notSoGreat: SortaHasNumberAndString = {
      name: "Long John Silver",
      age: 43,
      numberOfLimbs: 3
    }
    

    所以你不得不使用断言或其他技巧:

    const notSoGreat = {
      name: "Long John Silver",
      age: 43,
      numberOfLimbs: 3
    } as any as SortaHasNumberAndString; // okay now
    

    这意味着你遇到了接受这种类型的函数的麻烦:

    declare function acceptSortaHasNumberAndString(x: SortaHasNumberAndString): void;
    acceptSortaHasNumberAndString({name: "a", age: 123}); // error!
    

    不理想。


    另一种解决方案(我认为更好的解决方案)是允许HasNumberHasNumberAndString 在具有数字属性的键中成为generic。这意味着无需担心索引签名。

    type HasNumber<K extends keyof any> = Record<K, number>;
    type HasNumberAndString<K extends keyof any> = { name: string } & HasNumber<Exclude<K, "name">>;
    

    这使用内置的RecordExclude 类型别名来表示HasNumberAndString&lt;K&gt; 应该具有name 类型的string 属性,但所有其他属性都应该是number 类型。如果需要,您可以修改它们以反映不同的约束。让我们看看它是如何工作的:

    如果要谈HasNumberAndString类型的具体值,需要在类型中指定键:

    const hns: HasNumberAndString<'age'> = {
      name: "Harry James Potter",
      age: 38
    }
    

    这可能不是很好,但在大多数情况下,您可以让 TypeScript 为您推断这些键:

    const inferHasNumberAndString = 
      <T>(x: T & HasNumberAndString<keyof T>): HasNumberAndString<keyof T> => x;
    
    const hns = inferHasNumberAndString({
      name: "Harry James Potter",
      age: 38
    }); // inferred as type HasNumberAndString<"name" | "age">;
    

    上面的帮助函数inferHasNumberAndString 是通用的,它显示了您可以接受HasNumberAndString&lt;K&gt; 类型的值而无需自己强制执行K 的通用方法:

    declare function acceptHasNumberAndString<T>(x: T & HasNumberAndString<keyof T>): void;
    acceptHasNumberAndString({ name: "Hermione Granger", age: 39 });
    

    希望有所帮助;祝你好运!

    【讨论】:

      【解决方案2】:

      您可以使用union types 来允许stringnumber 类型的任何属性:

      interface HasNumber {
          [key: string]: number;
      }
      
      interface HasNumberAndString {
          [key: string]: string | number;
      }
      

      但这不会强制要求每种类型至少有一个属性。

      【讨论】:

      • 我可能只是将HasNumberAndString 的属性设为HasNumber 类型...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-18
      • 2021-08-02
      • 1970-01-01
      • 2018-11-06
      • 2016-09-15
      • 2019-10-21
      • 2014-03-28
      相关资源
      最近更新 更多