打字稿中的“静态方面”和“实例方面”究竟是什么?

Dan*_*iel 3 typescript

有一个非常相似的问题,但是文档中提到的静态端实例端没有直接答案。

正如我依稀理解的那样,静态端是构造函数,实例端是其他一切?

打字稿文档中的一个示例:

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick(): void;
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
Run Code Online (Sandbox Code Playgroud)

什么是静态的侧面实例方在这个例子吗?为什么需要使用这样的模式?

jca*_*alz 13

计算机科学中的命名是出了名的困难。

在 TypeScript 中,我们通常使用同名来指代类的构造函数(它是一个在运行时存在的值),并作为类的实例的类型(它只在设计时存在,不会发出到JavaScript)。

所以在

class Foo { } 
const foo: Foo = new Foo();
Run Code Online (Sandbox Code Playgroud)

有一个名为 的类构造函数Foo,它可以用来构造我们称之为 类型的类实例Foo

这有点像使用术语“乐高”来指代制造塑料建筑玩具的公司和建筑玩具本身。或者像“Toyota”作为公司名称和他们制造的车辆的名称。我们说“丰田制造并销售丰田汽车”或“乐高制造并销售乐高积木玩具”不会引起误解,但类似的“Foo构造Foo实例”可能会令人困惑。就这样吧,我猜。


当我们谈论一个类,我们可能会谈论它的静态方面,其中涉及到构造(一般奇异“构造”,因为这里的只是其中之一,这是相同的所有实例),或者我们可能会谈论它的实例方面,它与类的实例有关(通常是复数,“instance s ”,因为单个构造函数可以创建许多实例,每个实例可能具有与其他实例不同的状态或属性)。

从类型系统的角度来看,类型Foo对应于实例侧的形状。要获取静态端的类型,我们可以使用typeof类型查询运算符操作名为Foo: 的构造函数值typeof Foo。所以,Foo是实例端类型,typeof Foo是静态端类型。

让我们扩充Foo以展示它是如何工作的:

class Foo {

  instanceProp: string;

  constructor(constructorArgument: string) {
    this.instanceProp = constructorArgument;
  }

  instanceMethod(): void {
    console.log("Instance method called on " + this.instanceProp)
  }

  static staticProp: string = "Static Thing";

  static staticMethod(): void {
    console.log("Static method called on " + this.staticProp)
  }

}
Run Code Online (Sandbox Code Playgroud)

这是一个单一的类定义,但它有静态端和实例端。的静态部分Foo,即与其构造函数有关的内容,包括:

  • 构造函数本身,其签名是 {new(constructorArgument: string): Foo}
  • staticProp财产,其类型为string,和
  • staticMethod方法,其签名{(): void}

这种类型被称为typeof Foo,但如果需要,您实际上可以为它定义自己的接口,如下所示:

interface StaticSideOfFoo {
  new(constructorArgument: string): Foo;
  staticProp: string;
  staticMethod(): void;
}
Run Code Online (Sandbox Code Playgroud)

的实例侧Foo,即与其实例相关的事物,包括:

  • instanceProp财产,其类型为string,和
  • instanceMethod方法,其签名{(): void}

这种类型称为Foo,您也可以为此定义自己的接口:

interface InstanceSideOfFoo {
  instanceProp: string;
  instanceMethod(): void;
}
Run Code Online (Sandbox Code Playgroud)

让我们确保我们了解差异:

const foo1 = new Foo("Number 1");
console.log(foo1.instanceProp); // Number 1
foo1.instanceMethod(); // Instance method called on Number 1

const foo2 = new Foo("Number 2");
console.log(foo2.instanceProp); // Number 2
foo2.instanceMethod(); // Instance method called on Number 2

console.log(Foo.staticProp); // Static Thing
Foo.staticMethod(); // Static method called on Static Thing
Run Code Online (Sandbox Code Playgroud)

请注意,foo1foo2不能直接访问构造函数签名或访问staticPropstaticMethod

new foo1("oops"); // error!
foo1.staticProp; // error!
foo1.staticMethod(); // error!
Run Code Online (Sandbox Code Playgroud)

并且Foo无法直接访问instancePropinstanceMethod

Foo.instanceProp; // error!
Foo.instanceMethod(); // error!
Run Code Online (Sandbox Code Playgroud)

并且值foo1foo2具有类型Foo,类似于InstanceSideOfFoo,而值Foo具有类型typeof Foo,类似于StaticSideOfFoo。我们可以在这里验证:

const instanceSideOfFoo: InstanceSideOfFoo = foo1; // okay
const instanceSideOfFooOops: InstanceSideOfFoo = Foo; // error!

const staticSideOfFoo: StaticSideOfFoo = Foo; // okay
const staticSideOfFooOops: StaticSideOfFoo = foo1; // error!
Run Code Online (Sandbox Code Playgroud)

能够谈论这些不同的方面会有所帮助,因为它们具有不同的形状和不同的用途。如果你想接受的一个函数Foo的构造,你想谈论typeof FooStaticSideOfFoo或者new (x: string)=>Foo,而如果你想接受的实例的功能Foo,你想谈论FooInstanceSideOfFoo{instanceProp: string}

如果没有这样的区别,你最终可能会试图为你的孩子购买乐高公司,或者成为 1997 年丰田凯美瑞的股东。或者也许不是,但是 TypeScript 中的类似错误太容易犯了。我能说什么,命名事情很难。

好的,希望有帮助;祝你好运!

代码链接