【问题标题】:What's the difference between "Constructor", "Static" and regular interfaces in TypeScript?TypeScript 中的“构造函数”、“静态”和常规接口有什么区别?
【发布时间】:2019-09-17 07:01:08
【问题描述】:

TypeScript 有两个字符串接口,分别称为 StringStringConstructor

此外,TypeScript language specification 在第 1 章第 1.3 节中提供了此代码示例:

interface JQuery {
  text(content: string);
}

interface JQueryStatic {
  get(url: string, callback: (data: string) => any);
  (query: string): JQuery;
}

JQuery/JQueryStaticString/StringConstructor接口和做StaticConstructor接口有什么区别?

编辑: 是的,我知道链接的书 3 岁 已过时。

【问题讨论】:

    标签: typescript constructor interface static


    【解决方案1】:

    我要从String切换到RegExp,因为String有一个皱纹我稍后会提到*


    实例接口(如RegExpJQuery)通常表示可以存在多个不同实例的对象类型。关联的静态接口(如RegExpConstructorJQueryStatic)通常表示创建返回这些实例的对象类型;并且通常只有这些静态对象中的一个存在。所以只有一个RegExpConstructor 对象可以生成许多RegExp 对象,而只有一个JQueryStatic 对象可以生成许多JQuery 对象。

    实践中一个常见的混淆来源是name collision between values and types。单个静态对象的名称(例如,RegExpjQuery)往往与实例接口的名称相同。但是那个静态对象的类型不是实例接口的类型。因此,运行时名为RegExp 的类型为RegExpConstructor,而不是RegExp。而在运行时名为jQuery 的类型为JQueryStatic,而不是JQuery。这很令人困惑,但可能是最好的,因为它可以让你在运行时说出 x instanceof Y 之类的东西,而在编译时 x 的类型是 Y

    无论如何,如果有一个属性或方法的行为取决于特定的实例,它通常位于实例接口上。如果某些属性或方法的行为不依赖于特定实例,则通常位于静态接口上。


    构造函数接口是一个静态接口,专门允许您在其上使用new operator 来创建新实例。在 TypeScript 中,这类似于函数调用签名,但名称为 new,如下所示:

    type F = (x: string) => number[];
    type C = new(x: string) => number[]; 
    

    F 类型表示一个函数,它接受一个string 参数并生成一个numbers 数组,而C 类型表示一个构造函数,它接受一个@987654356 @ 参数并产生一个numbers 的数组:

    declare const f: F;
    declare const c: C;
    const arr1 = f("hey"); // number[]
    const oops1 = new f("hey"); // error, f is not newable
    const arr2 = new c("hey"); // number[]
    const oops2 = c("hey"); // error, c is not callable
    

    有一个静态接口也是一个构造函数接口是很常见的;所有class 静态接口都是构造函数接口。但并不是每个静态接口都是构造函数接口。 JQueryStatic 接口就是一个例子。要从 JQueryStatic 对象中获取 JQuery 实例,您可以像调用函数一样调用它(这就是 (query: string): JQuery; 签名的含义)。

    这就是JQueryStatic/JQuery 对和RegExpConstructor/RegExp 对之间的主要区别,也是问题主要答案的结尾。


    *回到String 皱纹。名为String 的类型特指通过在String 构造函数上调用new 运算符构造的object。还有一种名为string(带有小写“s”)的类型,它指的是primitive 数据类型。实际上,您处理的所有字符串都是string 类型的原语,而String 是一个相对不常见的wrapper object,它拥有string 值。 stringString 大多可以互换:

    const stringPrimitive = "hello"; // type is string
    const stringObject = new String("hello"); // type is String
    console.log(stringPrimitive+"!"); // "hello!"
    console.log(stringObject+"!"); // "hello!"
    console.log(stringPrimitive.charAt(4)); // "o"
    console.log(stringObject.charAt(4)); // "o"
    

    除非它们不可互换:

    console.log(typeof stringPrimitive); // "string"
    console.log(typeof stringObject); // "object"
    console.log(stringPrimitive instanceof String); // false
    console.log(stringObject instanceof String); // true
    

    当您意识到StringConstructor 也可以像函数一样被调用并产生一个原语string时,情况变得更加混乱:

    console.log(typeof "hey"); // "string"
    console.log(typeof new String("hey")); // "object"
    console.log(typeof String("hey")); // "string"
    

    所以这是一团糟。这里的规则几乎是always use string; never use String。这就是为什么我将代码示例从 String 更改为 always-an-object RegExp,没有原始数据类型 (typeof /foo/ === "object") 妨碍。


    好的,希望对您有所帮助。祝你好运!

    【讨论】:

    • 感谢您抽出宝贵的时间来回答这个问题,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2011-10-23
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多