【问题标题】:TS2322: Generic not propagated correctlyTS2322:泛型未正确传播
【发布时间】:2021-05-05 17:52:54
【问题描述】:

我们使用JSON:API 作为我们 API 的主要序列化模式。在不深入细节的情况下,它要求来自服务器的 JSON 响应具有顶级 data 属性,该属性可能包含单个实体一组实体:

// Single item
{ "data": { "id": 1 } }

// Collection of items
{ "data": [
    { "id": 1 },
    { "id": 2 }
] }

我将此模式编码为 TypeScript 类型,因此我们可以正确键入 API 响应。这产生了以下结果:

// Attributes as the base type for our entities
type Attributes = Record<string, any>;

// Single resource object containing attributes
interface ResourceObject<D extends Attributes> {
    attributes: D;
}

// Collection of resource objects
type Collection<D extends Attributes> = ResourceObject<D>[];

// Resource object OR collection, depending on the type of D
type ResourceObjectOrCollection<D extends Attributes | Attributes[]> = D extends Array<infer E>
    ? Collection<E>
    : ResourceObject<D>;

// A response with a resource object or a collection of items of the same type
interface ApiResponse<T> {
    data: ResourceObjectOrCollection<T>;
}

response 始终包含data 属性,该属性可以是单个resource object,也可以是资源对象的集合。
资源对象始终包含一个 attributes 属性,该属性包含任意实体属性。此处所有输入的目的是通过将实体接口作为通用 T 传递来传播属性结构,如下面的示例所示:

interface Cat {
    name: string;
}

function performSomeApiCall<Ret>(uri: string): Ret {
    return JSON.parse(''); // Stub, obviously
}

function single<T extends Attributes = Attributes>(uri: string): ApiResponse<T> {
    return performSomeApiCall<ApiResponse<T>>(uri);
}

single 函数从返回单个实体的端点获取响应 - 例如,/api/cats/42。因此,通过传递Cat 接口,数据类型可以正确解析为单个资源对象:

const name: string = single<Cat>('/api/cats/42').data.attributes.name;

我们还可以定义一个返回多个实体的函数:

function multiple<T extends Attributes = Attributes>(uri: string): ApiResponse<T[]> {
    return performSomeApiCall<ApiResponse<T[]>>(uri);
}

这个函数返回一个Ts的集合,所以下面的也是有效的:

const names: string[] = multiple<Cat>('/api/cats').data.map(item => item.attributes.name);

的作用是什么,但是,定义一个直接检索单个实体属性的函数,这是我不明白的:

function singleAttributes<T extends Attributes = Attributes>(uri: string): T {
    return single<T>(uri).data.attributes;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

类型 'Record' 不能分配给类型 'T'。
'Record' 可分配给类型'T' 的约束,但'T' 可以用约束'Record' 的不同子类型来实例化。 (2322)

为什么 Typescript 能理解 single 函数返回 T 的资源对象,而不是 singleAttributes?在某个地方,它似乎放弃了泛型类型,转而支持Attributes 的基本类型,但我不明白为什么或在哪里。

查看the playground link 以获得问题演示。

【问题讨论】:

  • 尝试从singleAttributes 中删除显式返回类型。在大多数情况下,TS 应该推断返回类型
  • 这似乎可行,但我仍然很好奇为什么 T 不是静态有效的返回类型?

标签: typescript json-api


【解决方案1】:

返回类型

function singleAttributes<T extends Attributes = Attributes>(uri: string): T {
    return single<T>(uri).data.attributes; < --- Record<string, any>
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

其实是Record&lt;string, any&gt;

见下一个例子:

function singleAttributes<T extends Attributes>(uri: string): T {
    let ret = single<T>(uri).data.attributes

    let t: T = null as any;
    ret = t; // ok
    t = single<T>(uri).data.attributes // error
}

类型 'Record' 不可分配给类型 'T'。 “Record”可分配给“T”类型的约束,但“T”可以用约束“Record”的不同子类型来实例化。

T 可以赋值给返回值,但返回值不能赋值给T。这就是为什么你可以使用T 作为返回类型的显式类型

T 可以是更宽的类型。

举例:

type WiderType = Record<string | symbol, any> extends Record<string, any> ? true : false // true


type WiderType2 = Record<string, any> extends Record<string | symbol, any> ? true : false // true

当您期望 Record&lt;stirng, any&gt; 时,T 也可以是 Record&lt;string | symbol,any&gt;。甚至Record&lt;string | symbol | number, any&gt;

为了让它发挥作用

从一开始就使用更宽的类型:

function singleAttributes<T extends Record<string | symbol | number, any>>(uri: string): T {
    return single<T>(uri).data.attributes
}

或删除显式返回类型

我会说Record&lt;string, any&gt;Record&lt;number, any&gt; 几乎相同,因为它将根据 js 规范推断为字符串。

Here你有很好的解释

这是按预期工作的,是 #16368 中引入的更严格检查的结果。在您的示例中, IMyFactoryType (或接口,它们在结构上是相同的)表示一个函数,该函数应该返回从 IMyObject 派生的任何类型的精确类型值,即使该函数实际上没有任何涉及 T 的参数将允许它发现 T 是什么并创建适当的返回值。换句话说:

【讨论】:

  • 让我点击的是链接问题中的代码注释:T 可以是派生自 IMyObject 的任何类型,但无法发现 T 是什么,因为它不是在参数类型中使用。
  • @MoritzFriedrich 看这里stackoverflow.com/questions/66410115/… 我正在收集有关协方差/方差和 inveriance 的所有链接。我敢打赌这对你来说会很有趣
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 2021-05-06
  • 2016-06-14
  • 2014-01-07
  • 1970-01-01
  • 2018-10-18
相关资源
最近更新 更多