Typescript 根据类的属性,类的泛型类型参数推断失败

Typescript 根据类的属性,类的泛型类型参数推断失败,typescript,Typescript,我有一个相当简单的例子,Typescript不推断类型。我想知道为什么Typescript会这样 类型脚本类型推断失败的情况 如果您有以下类声明: 声明类 { grunt():void } 声明抽象类部分{} 声明类Hoof扩展部分{} 以及以下功能: 声明函数getOwner(part:part):T; 然后,以下代码在Typescript编译器中失败: getOwner(newhoof()) .grunt(); 这是因为Typescript推断getOwner的返回类型为未知,而不是猪

我有一个相当简单的例子,Typescript不推断类型。我想知道为什么Typescript会这样

类型脚本类型推断失败的情况 如果您有以下类声明:

声明类
{
grunt():void
}
声明抽象类部分{}
声明类Hoof扩展部分{}
以及以下功能:

声明函数getOwner(part:part):T;
然后,以下代码在Typescript编译器中失败:

getOwner(newhoof())
.grunt();
这是因为Typescript推断getOwner的返回类型为
未知
,而不是

奇怪但有效的解决方案 但是,如果我只是在
部分的声明中添加一个属性
animal:T
代码将开始工作

换句话说,如果
部分的声明变成:

声明抽象类部分
{
动物:T
}
Typescript将能够推断
getOwner(newhoof())
的返回类型。我已经验证了Typescript 3.9.7和4.1.3的这种行为

问题: 为什么Typescript的行为是这样的?它是否应该保留泛型类型信息,即使
T
没有出现在任何类成员的签名中?

请参阅以获取规范答案

TypeScript的类型系统主要是结构化的,而不是名义上的。这意味着,如果比较类型
A
B
,当且仅当它们具有相同的结构(例如,两者都是具有相同键和相同值类型的成员的对象类型)时,才认为它们是相同的类型。命名什么类型的
A
B
(例如,我使用像“
A
”和“
B
”这样的名称是不相关的)或者它们被声明在哪里(例如,拥有像
接口A{A:string}
接口B{A:string}
这样的单独声明不会使它们成为单独的类型)


特别是,鉴于《宣言》

declare abstract class Part<T> { }
但以下任何一项都不会产生任何编译器错误:

var okay: Part<Swine>;
var okay: {}; // no error
var okay: Part<string>; // no error
var okay: Part<unknown>; // no error
声明抽象类部分{}
的翻译就是
(T:string)=>“{}”
)。由于
part(“猪”)
产生与
part(“string”}
或其他任何东西相同的输出,即字符串
“{}”
,因此无法编写反向函数
getOwnerType()
函数的输出实际上取决于其输入,因此情况变得更加合理:

function part(T: string): string {
  return "{animal: " + T + "}";
}
const hoof = part("Swine");
function getOwnerType(partT: string): string {
  return (partT.match(/^{animal: (.*)}$/) ?? ["", "unknown"])[1];
}
console.log(getOwnerType(hoof)); // Swine

function part(T: string): string {
  return "{}";
}
const hoof = part("Swine");
function getOwnerType(partT: string): string {
  // how would you implement this ???
  return "unknown";
}
console.log(getOwnerType(hoof)); // unknown
function part(T: string): string {
  return "{animal: " + T + "}";
}
const hoof = part("Swine");
function getOwnerType(partT: string): string {
  return (partT.match(/^{animal: (.*)}$/) ?? ["", "unknown"])[1];
}
console.log(getOwnerType(hoof)); // Swine