为什么推理站点在直接访问和由函数返回时有不同的效果

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

获取以下代码:

declare class BaseClass<TValue = any> {
  value: TValue;
  foo(value: TValue): void;
}

type Wrapped<T> = { value: T }

declare class ConcreteClasss<TValue> extends BaseClass<TValue> {
  constructor(value: Wrapped<TValue> | TValue);

  override foo(value: TValue | Wrapped<TValue>): void;
}

const valuesOf = <V>(base: BaseClass<V>): V => base.value;


const base = new ConcreteClasss('Alma');
    //^?

const value = base.value;
    //^? string

const value2 = valuesOf(base);
    //^? string | Wrapped<string>

游乐场

显然,

foo
方法用于推理站点(为什么不呢)。

但是为什么当通过辅助函数访问值时,值的类型会不同?

typescript typescript-generics inference
1个回答
0
投票

当您调用

valuesOf(base)
时,TypeScript 需要通过将
V
类型的值视为
ConcreteClass<string>
类型的值来推断
BaseClass<V>
。它本质上与
type V = ConcreteClass<string> extends BaseClass<infer V> ? V : never
相同。 这是
string | Wrapped<string>
,因为正如您所说,TypeScript 从
V
推断出
foo
(可能是因为方法的参数是双变的?如果您将
foo
定义为函数属性而不是方法,那么推断几乎将当然是不变的,你会得到
string
,但我认为这超出了所问问题的范围)。 这些类型根本不再记得
ConcreteClass<T>
;这些信息已被丢弃。因此,即使
valuesOf
的实现写为
base.value
,唯一可用的类型是
V
,即
string | WrappedString<T>

如果你想让

valuesOf(base)
返回
base.value
的实际类型,你需要将函数generic设为
base
的类型,例如:

const valuesOf =
  <B extends BaseClass<any>>(base: B): B['value'] => base.value

我已经注释了函数的返回类型以使用索引访问类型

B['value']
,否则你可能会得到
BaseClass<any>['value']
any
,因为TS将特定索引扩展到通用对象到约束,请参阅 microsoft/TypeScript#33181

现在你明白了

const value2 = valuesOf(base);
//    ^? const value2: string;

随心所欲。

Playground 代码链接

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