【问题标题】:Overriding interface property type defined in Typescript d.ts file覆盖 Typescript d.ts 文件中定义的接口属性类型
【发布时间】:2017-05-08 04:49:44
【问题描述】:

有没有办法改变 typescript 中 *.d.ts 中定义的接口属性类型?

例如: x.d.ts 中的接口定义为

interface A {
  property: number;
}

我想在我写入的打字稿文件中更改它

interface A {
  property: Object;
}

甚至这样也行

interface B extends A {
  property: Object;
}

这种方法行得通吗?当我在我的系统上尝试时,它不起作用。只是想确认它是否可能?

【问题讨论】:

    标签: javascript typescript typescript-typings


    【解决方案1】:

    您无法更改现有属性的类型。

    你可以添加一个属性:

    interface A {
        newProperty: any;
    }
    

    但是改变一种现有的类型:

    interface A {
        property: any;
    }
    

    导致错误:

    随后的变量声明必须具有相同的类型。多变的 'property' 必须是 'number' 类型,但这里有 'any' 类型

    您当然可以拥有自己的界面来扩展现有界面。在这种情况下,您可以仅将类型覆盖为兼容类型,例如:

    interface A {
        x: string | number;
    }
    
    interface B extends A {
        x: number;
    }
    

    顺便说一句,您可能应该避免使用 Object 作为类型,而是使用类型 any

    docs for the any type 中声明:

    any 类型是处理现有 JavaScript 的强大方式, 允许您在期间逐渐选择加入和退出类型检查 汇编。您可能希望 Object 扮演类似的角色,因为它 用其他语言做。 但是 Object 类型的变量只允许你 为它们分配任何值 - 你不能调用任意方法 它们,甚至是真实存在的

    let notSure: any = 4;
    notSure.ifItExists(); // okay, ifItExists might exist at runtime
    notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
    
    let prettySure: Object = 4;
    prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
    

    【讨论】:

    • 使用 Typescript >=1.1 通过扩展接口覆盖方法的类型,您需要包含原始接口中的所有方法,否则您将收到类型不兼容的错误,请参阅github.com/Microsoft/TypeScript/issues/978
    • 您可以先省略要覆盖的值,然后重新定义它们,我们可以让@ZSkycat 的答案解决吗?
    • 反对将 Java 称为“其他语言”
    • @wvdz 并不是我很在意反对票,但你在说什么?甚至有人在哪里提到过java?在页面中搜索“java”只有一个发现,它在您的评论中。
    • 哎呀,对不起,我的错。 Tbh,即使是你的话,我真的只是有点脾气暴躁。很遗憾,无法再删除反对票。
    【解决方案2】:

    有趣的是,我花了一天时间调查解决同一案件的可能性。 我发现这样做是不可能的:

    // a.ts - module
    export interface A {
        x: string | any;
    }
    
    // b.ts - module
    import {A} from './a';
    
    type SomeOtherType = {
      coolStuff: number
    }
    
    interface B extends A {
        x: SomeOtherType;
    }
    

    原因模块可能不知道您的应用程序中的所有可用类型。从任何地方移植所有内容并编写这样的代码是非常无聊的。

    export interface A {
        x: A | B | C | D ... Million Types Later
    }
    

    您必须稍后定义类型才能使自动完成功能正常工作。


    所以你可以作弊一点:

    // a.ts - module
    export interface A {
        x: string;
    }
    

    默认情况下保留 some 类型,当不需要覆盖时允许自动完成工作。

    然后

    // b.ts - module
    import {A} from './a';
    
    type SomeOtherType = {
      coolStuff: number
    }
    
    // @ts-ignore
    interface B extends A {
        x: SomeOtherType;
    }
    

    在这里使用@ts-ignore 标志禁用愚蠢的异常,告诉我们我们做错了什么。有趣的是,一切都按预期进行。

    在我的例子中,我正在缩小 x 类型的范围,它允许我执行更严格的代码。例如,您有 100 个属性的列表,然后将其减少到 10 个,以避免出现愚蠢的情况

    【讨论】:

      【解决方案3】:

      我使用的方法是先过滤字段,然后合并它们。

      参考Exclude property from type

      interface A {
          x: string
      }
      
      export type B = Omit<A, 'x'> & { x: number };
      

      接口:

      interface A {
          x: string
      }
      
      interface B extends Omit<A, 'x'> {
        x: number
      }
      

      【讨论】:

      • 很高兴知道这一点。但问题是它仍然没有修改现有的。
      • 这正是我想要的。这就是我期望打字稿extend 默认工作的方式,但是唉,这个小Omit 修复了一切?
      • 扩展界面正是我想要的,谢谢!
      • 注意:您需要上面的 typescript 3.5.3 才能使用它。
      • 节省我的时间!谢谢?
      【解决方案4】:

      稍微扩展@zSkycat 的答案,您可以创建一个接受两种对象类型并返回合并类型的泛型,其中第二个的成员覆盖第一个的成员。

      type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
      type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
      
      interface A {
          name: string;
          color?: string;
      }
      
      // redefine name to be string | number
      type B = Merge<A, {
          name: string | number;
          favorite?: boolean;
      }>;
      
      let one: A = {
          name: 'asdf',
          color: 'blue'
      };
      
      // A can become B because the types are all compatible
      let two: B = one;
      
      let three: B = {
          name: 1
      };
      
      three.name = 'Bee';
      three.favorite = true;
      three.color = 'green';
      
      // B cannot become A because the type of name (string | number) isn't compatible
      // with A even though the value is a string
      // Error: Type {...} is not assignable to type A
      let four: A = three;
      

      【讨论】:

      • 非常酷 :-) 我以前用 Omit 用一两个属性做过这个,但这更酷 :-) 我经常想“扩展”一个服务器实体类型并改变一些东西在客户端上是必需的或可选的。
      • 这应该是现在公认的解决方案。 “扩展”界面的最简洁方式。
      【解决方案5】:
       type ModifiedType = Modify<OriginalType, {
        a: number;
        b: number;
      }>
       
      interface ModifiedInterface extends Modify<OriginalType, {
        a: number;
        b: number;
      }> {}
      

      ZSkycat'sextends Omit 解决方案的启发,我想出了这个:

      type Modify<T, R> = Omit<T, keyof R> & R;
      
      // before typescript@3.5
      type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R
      

      例子:

      interface OriginalInterface {
        a: string;
        b: boolean;
        c: number;
      }
      
      type ModifiedType  = Modify<OriginalInterface , {
        a: number;
        b: number;
      }>
      
      // ModifiedType = { a: number; b: number; c: number; }
      

      一步一步来:

      type R0 = Omit<OriginalType, 'a' | 'b'>        // { c: number; }
      type R1 = R0 & {a: number, b: number }         // { a: number; b: number; c: number; }
      
      type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
      type T1 = Pick<OriginalType, T0>               // { c: number; }
      type T2 = T1 & {a: number, b: number }         // { a: number; b: number; c: number; }
      

      TypeScript Utility Types


      v2.0 深度修改

      interface Original {
        a: {
          b: string
          d: {
            e: string // <- will be changed
          }
        }
        f: number
      }
      
      interface Overrides {
        a: {
          d: {
            e: number
            f: number // <- new key
          }
        }
        b: {         // <- new key
          c: number
        }
      }
      
      type ModifiedType = ModifyDeep<Original, Overrides>
      interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
      
      // ModifiedType =
      {
        a: {
          b: string
          d: {
            e: number
            f: number
          }
        }
        b: {
          c: number
        }
        f: number
      }
      

      找到ModifyDeepbelow

      【讨论】:

      • 这是一个很好的解决方案。
      • Noob 在这里,但在您的示例中,您正在从接口更改为类型,不是吗?还是没有区别?
      • Niceeeeeeeeeee :D
      • @Dominic 好点,我已经更新了答案。两个同名的接口可以合并。 typescriptlang.org/docs/handbook/…
      • @Donnovan 是自定义类型,再过一遍答案找到->type Modify&lt;T, R&gt; = Omit&lt;T, keyof R&gt; &amp; R;
      【解决方案6】:

      如果其他人需要通用实用程序类型来执行此操作,我想出了以下解决方案:

      /**
       * Returns object T, but with T[K] overridden to type U.
       * @example
       * type MyObject = { a: number, b: string }
       * OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
       */
      export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };
      

      我需要这个,因为在我的情况下,覆盖的关键是泛型本身。

      如果您没有准备好Omit,请参阅Exclude property from type

      【讨论】:

      • 这正是我要找的,我感激不尽 :D :D :D
      • @dwoodwardgb 很高兴它对其他人有用:-)
      【解决方案7】:

      对于缩小属性的类型,简单的extend 效果很好,如Nitzan's answer

      interface A {
          x: string | number;
      }
      
      interface B extends A {
          x: number;
      }
      

      为了扩大,或一般覆盖类型,你可以做Zskycat's solution:

      interface A {
          x: string
      }
      
      export type B = Omit<A, 'x'> & { x: number };
      

      但是,如果您的接口A 扩展了一个通用接口,那么在使用Omit 时,您将失去A 剩余属性的自定义类型。

      例如

      interface A extends Record<string | number, number | string | boolean> {
          x: string;
          y: boolean;
      }
      
      export type B = Omit<A, 'x'> & { x: number };
      
      let b: B = { x: 2, y: "hi" }; // no error on b.y! 
      

      原因是,Omit 在内部只遍历 Exclude&lt;keyof A, 'x'&gt; 键,在我们的例子中这将是一般的 string | number。因此,B 将变为 {x: number; } 并接受 number | string | boolean 类型的任何额外属性。


      为了解决这个问题,我想出了一个不同的OverrideProps 实用程序类型,如下所示:

      type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };
      

      例子:

      type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };
      
      interface A extends Record<string | number, number | string | boolean> {
          x: string;
          y: boolean;
      }
      
      export type B = OverrideProps<A, { x: number }>;
      
      let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!
      

      【讨论】:

        【解决方案8】:

        Omit扩展接口时的属性:

        interface A {
          a: number;
          b: number;
        }
        
        interface B extends Omit<A, 'a'> {
          a: boolean;
        }
        

        【讨论】:

          【解决方案9】:

          注意:不确定我在此答案中使用的语法在编写旧答案时是否可用,但 我认为这是解决问题的更好方法这个问题中提到的例子。


          我遇到了一些与此主题相关的问题(覆盖接口属性),这就是我的处理方式:

          1. 首先创建一个通用接口,其中包含您想要使用的可能类型。

          您甚至可以为通用参数选择一个default 值,正如您在&lt;T extends number | SOME_OBJECT = number&gt; 中看到的那样

          type SOME_OBJECT = { foo: "bar" }
          
          interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
            property: T;
          }
          
          1. 然后您可以通过将值传递给泛型参数(或省略它并使用默认值)来基于该协定创建新类型:
          type A_NUMBER = INTERFACE_A;                   // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
          type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT>  // MAKES { property: SOME_OBJECT }
          

          结果如下:

          const aNumber: A_NUMBER = {
              property: 111  // THIS EXPECTS A NUMBER
          }
          
          const anObject: A_SOME_OBJECT = {
              property: {   // THIS EXPECTS SOME_OBJECT
                  foo: "bar"
              }
          }
          

          Typescript playground

          【讨论】:

            【解决方案10】:

            像我这样的懒人的简短回答:

            type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> }; 
            
            interface Overrided extends Omit<YourInterface, 'overrideField'> {
              overrideField: <type>
            }
            

            【讨论】:

              【解决方案11】:

              我创建了这种类型,可以轻松覆盖嵌套接口:

              type ModifyDeep<A extends AnyObject, B extends DeepPartialAny<A>> = {
                [K in keyof A]: B[K] extends never
                  ? A[K]
                  : B[K] extends AnyObject
                    ? ModifyDeep<A[K], B[K]>
                    : B[K]
              } & (A extends AnyObject ? Omit<B, keyof A> : A)
              
              /** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
              type DeepPartialAny<T> = {
                [P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
              }
              
              type AnyObject = Record<string, any>
              

              然后你可以这样使用它:

              interface Original {
                a: {
                  b: string
                  d: {
                    e: string // <- will be changed
                  }
                }
                f: number
              }
              
              interface Overrides {
                a: {
                  d: {
                    e: number
                    f: number // <- new key
                  }
                }
                b: {         // <- new key
                  c: number
                }
              }
              
              type ModifiedType = ModifyDeep<Original, Overrides>
              interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
              
              // ModifiedType =
              {
                a: {
                  b: string
                  d: {
                    e: number
                    f: number
                  }
                }
                b: {
                  c: number
                }
                f: number
              }
              

              【讨论】:

              • 找不到名字Obj
              • 我自己创建了一个深度修改类型,没有你的DeepPartialAny,我无法让它工作。否则我会收敛到与你基本相同的解决方案,所以我决定不在my answer 中包含我的版本,而是更新和改进你的。
              • 这真的很难理解。你认为你可以让代码更冗长一点吗?
              • 我能够得到这个工作的like so
              【解决方案12】:

              日期:2021 年 3 月 19 日。 我认为最新的 typescript(4.1.2) 版本支持 interfaced.ts 文件中覆盖。

              // in test.d.ts
              
              interface A {
                a: string
              }
              
              export interface B extends A {
                a: number
              }
              
              // in any ts file
              import { B } from 'test.d.ts'
              
              // this will work
              const test: B = { a: 3 }
              
              // this will not work
              const test1: B = { a: "3" }
              
              

              【讨论】:

              • 20201了吗?哈哈。你是从未来和我们说话。那里有飞行汽车吗?
              • 哈哈哈,我想说是的,汽车在飞,人们在旅行。 :-) 感谢您指出错字!!!
              • 我来自未来。它不起作用incorrectly extends interface
              • 您的ts 版本是什么?您是否在d.ts 文件中声明了接口? @victorzadorozhnyy
              • @yongmingzhuang 4.2 我已经尝试在 tsx 中实现了
              【解决方案13】:

              如果你只想修改现有属性的类型而不删除它,那么 & 就足够了:

              // Style that accepts both number and percent(string)
              type BoxStyle = {
                height?: string | number,
                width?: string | number,
                padding?: string | number,
                borderRadius?: string | number,
              }
              
              // These are both valid
              const box1: BoxStyle = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
              const box2: BoxStyle = {height: 85, width: 85, padding: 0, borderRadius: 5}
              
              // Override height and width to be only numbers
              type BoxStyleNumeric = BoxStyle & {
                height?: number,
                width?: number,
              }
              
              // This is still valid
              const box3: BoxStyleNumeric = {height: 85, width: 85, padding: 0, borderRadius: 5}
              
              // This is not valid anymore
              const box4: BoxStyleNumeric = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
              

              【讨论】:

                猜你喜欢
                • 2016-02-15
                • 2018-08-18
                • 2018-05-18
                • 2016-10-18
                • 2017-08-22
                • 2016-01-02
                • 2023-03-21
                • 1970-01-01
                • 2014-11-21
                相关资源
                最近更新 更多