我可以强制 TypeScript 编译器使用名义类型吗?
Can I force the TypeScript compiler to use nominal typing?
TypeScript 使用 structural subtyping,所以这实际上是可能的:
// there is a class
class MyClassTest {
foo():void{}
bar():void{}
}
// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
if (f){
if (!(f instanceof MyClassTest)) {
console.log("why")
}
}
}
// but it's valid to pass objects, which aren't in fact instances
// they just need to "look" the same, be "structural subtypes"
someFunction({foo(){}, bar(){}}) //valid!
然而,作为 someFunction
的实现提供者,我真的想禁止传递结构相似的对象,但我真的只想允许 MyClassTest 或其子类型的真实实例。我想至少对我自己的一些函数声明强制执行 "nominal typing"。
这可能吗?
背景:考虑传递给 API 的对象需要属于该类型的情况,例如因为它们是由工厂生产的,该工厂在此对象上设置了一些内部状态,并且该对象实际上有一个私有接口,someFunction
期望该接口能够正常工作。但是我不想透露那个私有接口(例如在打字稿定义文件中),但我希望如果有人传入一个伪造的实现,它是一个编译器错误。具体示例:我希望打字稿编译器在这种情况下会抱怨,即使我像这样提供所有成员:
//OK for typescript, breaks at runtime
window.document.body.appendChild({nodeName:"div", namespaceURI:null, ...document.body})
没有使编译器以名义方式运行的编译器标志,实际上原则上不能有这样的标志,因为它会破坏很多 javascript 场景。
有几种技术通常用于模拟某种名义类型。品牌类型通常用于基元(从 here 中选择一个作为样本),对于 类 一种常见的方法是添加一个私有字段(私有字段在结构上不会与任何东西匹配,除了确切的私有字段定义)。
使用最后一种方法,您的初始样本可以写成:
// there is a class
class MyClassTest {
private _useNominal: undefined; // private field to ensure nothing else is structually compatible.
foo():void{}
bar():void{}
}
// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
if (f){
if (!(f instanceof MyClassTest)) {
console.log("why")
}
}
}
someFunction({foo(){}, bar(){}, _useNominal: undefined}) //err
对于您的特定场景,使用 append
我不认为我们可以做任何我们可以做的事情,因为 Node
在 lib.d.ts
中定义为接口和 const
,因此用私有字段扩充它几乎是不可能的(不更改 lib.d.ts
),即使我们可以,它也可能会破坏许多现有代码和定义。
TypeScript 使用 structural subtyping,所以这实际上是可能的:
// there is a class
class MyClassTest {
foo():void{}
bar():void{}
}
// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
if (f){
if (!(f instanceof MyClassTest)) {
console.log("why")
}
}
}
// but it's valid to pass objects, which aren't in fact instances
// they just need to "look" the same, be "structural subtypes"
someFunction({foo(){}, bar(){}}) //valid!
然而,作为 someFunction
的实现提供者,我真的想禁止传递结构相似的对象,但我真的只想允许 MyClassTest 或其子类型的真实实例。我想至少对我自己的一些函数声明强制执行 "nominal typing"。
这可能吗?
背景:考虑传递给 API 的对象需要属于该类型的情况,例如因为它们是由工厂生产的,该工厂在此对象上设置了一些内部状态,并且该对象实际上有一个私有接口,someFunction
期望该接口能够正常工作。但是我不想透露那个私有接口(例如在打字稿定义文件中),但我希望如果有人传入一个伪造的实现,它是一个编译器错误。具体示例:我希望打字稿编译器在这种情况下会抱怨,即使我像这样提供所有成员:
//OK for typescript, breaks at runtime
window.document.body.appendChild({nodeName:"div", namespaceURI:null, ...document.body})
没有使编译器以名义方式运行的编译器标志,实际上原则上不能有这样的标志,因为它会破坏很多 javascript 场景。
有几种技术通常用于模拟某种名义类型。品牌类型通常用于基元(从 here 中选择一个作为样本),对于 类 一种常见的方法是添加一个私有字段(私有字段在结构上不会与任何东西匹配,除了确切的私有字段定义)。
使用最后一种方法,您的初始样本可以写成:
// there is a class
class MyClassTest {
private _useNominal: undefined; // private field to ensure nothing else is structually compatible.
foo():void{}
bar():void{}
}
// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
if (f){
if (!(f instanceof MyClassTest)) {
console.log("why")
}
}
}
someFunction({foo(){}, bar(){}, _useNominal: undefined}) //err
对于您的特定场景,使用 append
我不认为我们可以做任何我们可以做的事情,因为 Node
在 lib.d.ts
中定义为接口和 const
,因此用私有字段扩充它几乎是不可能的(不更改 lib.d.ts
),即使我们可以,它也可能会破坏许多现有代码和定义。