打字稿中类的“静态端”和“实例端”到底是什么?

问题描述 投票:0回答:1

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

据我隐约了解,静态侧是构造函数,实例侧是其他一切?

来自 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);

本例中的静态侧实例侧是什么?为什么需要使用这样的模式?

typescript
1个回答
20
投票

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

在 TypeScript 中,我们通常使用相同的名称来引用类的 constructor(这是运行时存在的值),并作为类的 instances 的类型(仅在设计时存在)并且不会发送到 JavaScript)。

所以在

class Foo { } 
const foo: Foo = new Foo();

有一个名为

Foo
的类构造函数,可用于构造类型为
Foo
的类实例。

这有点像使用“乐高”一词来指代生产塑料拼装玩具的公司和拼装玩具本身。或者像“丰田”作为公司名称及其制造的车辆的名称。我们说“丰田制造和销售丰田汽车”或“乐高制造和销售乐高建筑玩具”不会产生误解,但类似的“

Foo
构建
Foo
实例”可能会令人困惑。我想就这样吧。


当我们谈论一个类时,我们可能会谈论它的static方面,这些方面与构造函数有关(通常是单数,“the构造函数”,因为只有其中一个,这对于所有实例都是相同的) ,或者我们可能正在谈论它的 instance 方面,这些方面与类的实例有关(通常是复数,“instances”,因为单个构造函数可以创建许多实例,每个实例可能具有不同的状态或属性来自其他人)。

从类型系统的角度来看,类型

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)
  }

}

这是一个单一的类定义,但它有一个静态端和一个实例端。

Foo
的静态方面,即与其构造函数有关的内容,包括:

  • 构造函数本身,其签名为
    {new(constructorArgument: string): Foo}
  • staticProp
    属性,其类型为
    string
    ,并且
  • staticMethod
    方法,其签名为
    {(): void}

这种类型称为

typeof Foo
,但如果您愿意,您实际上可以为其定义自己的接口,如下所示:

interface StaticSideOfFoo {
  new(constructorArgument: string): Foo;
  staticProp: string;
  staticMethod(): void;
}

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

  • instanceProp
    属性,其类型为
    string
    ,并且
  • instanceMethod
    方法,其签名为
    {(): void}

这种类型称为

Foo
,您也可以为此定义自己的接口:

interface InstanceSideOfFoo {
  instanceProp: string;
  instanceMethod(): void;
}

让我们确保我们理解其中的区别:

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

请注意,

foo1
foo2
无法直接访问构造函数签名或
staticProp
staticMethod

new foo1("oops"); // error!
foo1.staticProp; // error!
foo1.staticMethod(); // error!

并且

Foo
无法直接访问
instanceProp
instanceMethod
:

Foo.instanceProp; // error!
Foo.instanceMethod(); // error!

foo1
foo2
的类型为
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!

能够谈论这些不同的侧面是有帮助的,因为它们有不同的形状和不同的用途。如果你想创建一个接受

Foo
构造函数的函数,你需要谈论
typeof Foo
StaticSideOfFoo
new (x: string)=>Foo
,而如果你想创建一个接受
Foo
实例的函数,你想谈谈
Foo
InstanceSideOfFoo
{instanceProp: string}

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

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

代码链接

© www.soinside.com 2019 - 2024. All rights reserved.