TypeScript 对象字面量也应该有一个类型,

zfciruhq  于 6个月前  发布在  TypeScript
关注(0)|答案(3)|浏览(45)

TypeScript版本: 3.7.0-dev.20190831
搜索词:this 返回类型对象组合
代码

我尝试将对象的一些部分作为多重继承或对象组合的形式编写,稍后将其组合在一起,并遇到了这种情况。

const a = {
	Clone() {
		return this;
	},
	x: 1,
};

const b = {
	Clone: a.Clone,
	y: 2,
};

console.log(b.Clone().x); // number, bad! Should be an error!
console.log(b.Clone().y); // compiler error, bad! This should be acceptable!

预期行为:Clone 的返回类型 typeof b ,在 b.Clone() 中应该计算为 typeof a
实际行为: 它计算为 a ,因为它在 this 中定义了。

可以通过将 this 定义为类型参数来绕过这个问题:

const a = {
	Clone<T>(this: T) {
		return this;
	},
	x: 1,
}

const b = {
	Clone: a.Clone,
	y: 2
};

console.log(b.Clone().x); // compiler error, good!
console.log(b.Clone().y); // works! Also good!

然而,这里还有一个有趣的问题,即使用 this 的类型参数也意味着 TS 不知道 this 中可能包含什么。这意味着你必须手动指定希望访问的所有成员。如果能做 T extends typeof a 并自动删除 a 中的所有成员就好了。现在,如果你这样做,会出现错误:
'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)
我认为所有这样的方法都应该隐式等价于以下内容:

const a = {
	Clone<T extends typeof a>(this: T) {
		console.log(this.x); // should work
		return this;
	},
	x: 1,
}

然后,由于 b 不是 a 的超集,因此 b.Clone() 将出错。除非你做了 c = Object.assign({}, a, b) 并执行了 c.Clone()

**Playground链接:**https://www.typescriptlang.org/play/?target=6#code/MYewdgzgLgBAhjAvDA3gKAJAGEA24CmAFAJSqYYBO+UArhWDFABYCWEA3JgL4A0mAHgC4YARjRdOaUJFgAjJGWx4w+YXAB0uAnwwBPYQCZxk6RBA586vAHNCszcqLF1-YuxgB6DzBpgAJvgAZiwqflLgZhZWILb2Wiok6rpunt6gALYADiwWFDD4FBQgFEA
相关问题:#29122

pbpqsu0x

pbpqsu0x1#

我非常感谢你在这里抽象地表达的观点,因为这在理论上可以消除你所强调的错误;然而,其含义是 this 应该根据上下文动态地而不是静态地进行类型化,以便它最终相对于调用路径中的父级,这将给类型系统带来巨大的负担。
例如,在下面的示例中, this 在每种情况下都在动态地引用不同的东西...但你是希望类型系统能够意识到每种情况下 this 的上下文值吗?

var whatIsX = function () {
	return this.x; // this=outer scope --> x=3
}

var a = {
	whatIsX: whatIsX,
	x: 1
};

var b = {
	whatIsX: whatIsX,
	y: 2
};

var x = 3;
whatIsX(); // -> this=outer scope --> x=3
a.whatIsX() // -> this=a --> x=1
b.whatIsX() // -> this=b --> x=undefined

TS Playground

mwkjh3gx

mwkjh3gx2#

要使此工作,编译器需要为所有匿名类型维护一个隐藏的 this 类型参数,就像它为类类型所做的那样——基本上做你第二个、可行的示例所做的事。对于类来说,隐藏的类型参数已经很昂贵了,但匿名类型更加常见。
为了继续进行这项工作,我们需要三件事:
1.大量的赞同。
1.估计其成本有多高。你可能需要一个原型加上性能测试套件的运行。
1.估计有多少代码会受到影响(可能不会有任何影响)。
而不是(2)和(3),你可能会提出如何确保这只对选定的对象字面量发生,以便保持编译时间成本低的建议。

3bygqnnd

3bygqnnd3#

为了使这个工作,编译器需要为所有匿名类型维护一个隐藏的 this 类型参数。并非完全如此 - interface 只在需要时引入 this 类型(我记得是这样的)。你可能只能对具有方法的对象或其属性是非箭头 function 表达式的对象这样做。

相关问题