有一个非常相似的问题,但是对于文档中提到的静态端和实例端没有直接答案。
据我隐约了解,静态侧是构造函数,实例侧是其他一切?
来自 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 中,我们通常使用相同的名称来引用类的 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 中类似的错误太容易犯了。我能说什么,命名事物很难。
好的,希望有帮助;祝你好运!