代码之家  ›  专栏  ›  技术社区  ›  Morgan Touverey Quilling

重写TypeScript中接口的属性

  •  13
  • Morgan Touverey Quilling  · 技术社区  · 6 年前

    我知道,禁止在扩展接口中重写接口的属性,修改其类型。

    我正在寻找一种替代解决方案,它允许我不复制第一个界面的内容(它非常大)。

    下面是我的第一个天真的方法。考虑到基本接口:

    interface OrginalInterface {
        title?: string;
        text?: string;
        anotherProperty?: SomeType;
        // lots of other properties
    }
    

    此接口在库中定义。 我不能仅仅为了满足我在扩展接口中的需要而修改它(例如,添加泛型)。

    在包装器库(我的)使用的扩展接口中,我希望重用现有接口,同时使一些字段具有不同的类型:

    interface ExtendedInterface extends OriginalInterface {
        title?: string | ReactElement<any>;
        text?: string | ReactElement<any>;
    }
    

    但这是不可能的。

    error TS2430: Interface 'ExtendedInterface' incorrectly extends interface 'OriginalInterface'.
      Types of property 'title' are incompatible.
        Type 'ReactElement<any>' is not assignable to type 'string'.
    

    我还尝试将两个接口合并在一起:

    type Extended = OriginalInterface & NewInterfaceWithOverridingPropertiesOnly;
    

    虽然这通过了编译,但它不起作用。如果使用此类型声明变量,则只能为具有兼容结构的对象赋值 OriginalInterface

    我觉得TypeScript的类型系统没有提供任何其他方式来表达我声明新类型的需要 派生的 从…起 OrginalInterface 。我不需要将新类型分配给 原始接口 ;我只需要它来重用 原始接口

    我需要一些像映射类型这样的东西,这些类型具有影响属性的条件。大概 Conditional types 来自预发布的TypeScript 2.8?还是应该复制第一个界面的内容?

    5 回复  |  直到 6 年前
        1
  •  22
  •   Community Egal    4 年前

    更新,2018-08

    引入TypeScript 2.8 Exclude<T, U> 其行为类似于 Diff<T, U> 下面定义了所有类型(不仅仅是键类型)。你绝对应该使用 Exclude<> 而不是 Diff<> 如果您使用的是TypeScript 2.8或更高版本。

    此外,在TypeScript 2.9中, keyof any 已从扩展 string string | number | symbol ,所以下面 差异(&L);T、 U> 导致的错误可以通过更改 Diff<T extends string, U extends string> Diff<T extends keyof any, U extends keyof any> 。下面进行了此更改。


    原始答案,2018-03

    conditional types will enable this 虽然 you can get this behavior without them

    其想法是从原始接口中提取属性,然后用新的属性替换它们。像这样:

    type Diff<T extends keyof any, U extends keyof any> = 
      ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
    type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
    
    interface OriginalInterface {
      title?: string;
      text?: string;
      anotherProperty?: SomeType;
      // lots of other properties
    }
    interface Extension {
      title?: string | ReactElement<any>;
      text?: string | ReactElement<any>;
    }
    
    interface ExtendedInterface extends Overwrite<OriginalInterface, Extension> {};
    
    const ext: ExtendedInterface = {};  // okay, no required properties
    ext.text; // string | ReactElement<any> | undefined
    ext.title; // string | ReactElement<any> | undefined
    ext.anotherProperty; // SomeType | undefined
    

    编辑:我更改了 Overwrite<T,U> 尊重物业的可选/必需状态 T 其密钥不在中 U

    我想这是你想要的行为。希望有帮助;祝你好运

        2
  •  7
  •   Felix    4 年前

    使用TypeScript Omit :

    忽略(<);T、 K>

    通过从T中拾取所有属性,然后删除K来构造类型

    // original interface
    interface A {
      a: number;
      b: number; // we want string type instead of number
    }
    
    // Remove 'b'
    type BTemp = Omit<A, 'b'>;
    
    // extends A (BTemp) and redefine b
    interface B extends BTemp {
      b: string;
    }
    
    const a: B = {
      a: 5,
      b: 'B'
    }
    

    enter image description here

        3
  •  3
  •   Tucaen    6 年前

    自2.9起的中断更改: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#keyof-now-includes-string-number-and-symbol-keys

    使用

    Extract<keyof T, string> 
    

    避免

    类型“keyof T”不满足约束“string”。

    编辑:本会对此发表评论,但缺乏必要的声誉

        4
  •  1
  •   Masih Jahangiri    3 年前

    简短回答:

    type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> }; 
    
        5
  •  0
  •   mwieczorek    4 年前

    我总是用这个来约会。我有一个公共库,用于后端和前端React应用程序,用于域特定实体的类型定义。

    通过数据库接收数据时,日期字段为 Date 对象,但当REST API使用时,日期是字符串对象(ISO日期)。由于对这两个库都有单独的接口定义是不现实的(这会破坏拥有公共库的目的),因此我在公共库中定义类型如下:

    type IsoDateString = string
    type DatesAsIsoString<T, U extends keyof T> = Omit<T, U> & { [key in U]: IsoDateString }
    
    // Example object
    interface User {
      id: number
      registeredAt: Date
    }
    
    // on the back end
    const user: User = await getUserFromDatabase()
    console.log(user.registeredAt) // Date
    user.registeredAt.getTime() // ms epoch
    
    // on the front end, define
    type FetchedUser = DatesAsIsoString<User, 'registeredAt'>
    
    // then use
    const user: FetchedUser = await getUserFromApi()
    console.log(user.registeredAt) // string, e.g. 2020-08-25T05:52:03.000Z
    const actualDate = new Date(user.registeredAt) // date object