如何正确推断 TypeScript 中嵌套和复杂类型?

huangapple go评论72阅读模式
英文:

How to properly infer nested and complex types in Typescript?

问题

我认为有时候 TypeScript 在处理复杂和嵌套类型时无法正确地进行类型推断。

我需要知道这是否是一个 bug,还是我做错了。如果我做错了,那么实现嵌套和复杂类型的推断的最佳方法是什么?

到目前为止,我提出了以下代码:

// Storable 类型是具有可选 key 属性的对象
type Storable<O, Key extends string> = O & { [key in Key]?: string };
// Stored 类型是具有必需 key 属性的对象
type Stored<S> = S extends Storable<infer O, infer Key extends string> ? O & { [key in Key]: string } : never

// MyObject 是一个标准对象
type MyObject = { prop1: boolean; prop2: number; };
// MyStorableObject 是一个 Storable MyObject
type MyStorableObject = Storable<MyObject, "id">;

// 将一个 Storable 对象转换为 Stored 对象
function store<T, B extends string>(object: Storable<T, B>, key: B): Stored<T> {
  return { ...object, [key]: 'generatedId' } as unknown as Stored<T>;
}

当我调用 store 函数时,我期望 stored 不是 never 类型:

const storableObject: MyStorableObject = { prop1: true, prop2: 0 };
const stored = store(storableObject, 'id');

奇怪的是,有时它工作正常,有时它不工作。例如,使用这个 MyObject 类型:

type MyObject = { prop1: string };

存储的对象类型是我所期望的:

const stored: MyObject & {
    id?: string | undefined;
} & {
    id: string;
    prop1: string;
}
英文:

I think sometimes Typescript does not infer properly when it comes to complex and nested types.

I need to know if it's a bug or if I'm doing wrong. And if I'm doing wrong, what is the best way to achieve inference of nested and complex types ?

I came up with this code so far:

// Storable type is the Object with an optional key prop
type Storable&lt;O, Key extends string&gt; = O &amp; { [key in Key]?: string };
// Stored type is the Object with a required key prop
type Stored&lt;S&gt; = S extends Storable&lt;infer O, infer Key extends string&gt; ? O &amp; { [key in Key]: string} : never

// MyObject is a standard object
type MyObject = { prop1: boolean; prop2: number; };
// MyStorableObject is a Storable MyObject
type MyStorableObject = Storable&lt;MyObject, &quot;id&quot;&gt;;

// Transforms a Storable object to a Stored object
function store&lt;T, B extends string&gt;(object: Storable&lt;T, B&gt;, key: B): Stored&lt;T&gt; {
  return { ...object, [key]: &#39;generatedId&#39; } as unknown as Stored&lt;T&gt;;
}

When I call store function, I expect stored not to be never type :

const storableObject:MyStorableObject = { prop1: true, prop2: 0 };
const stored = store(storableObject, &#39;id&#39;);

What is weird is that sometimes, it works, sometimes it doesn't. For example, with this MyObject type :

type MyObject = { prop1: string };

The stored object type is what I expect:

const stored: MyObject &amp; {
    id?: string | undefined;
} &amp; {
    id: string;
    prop1: string;
}

答案1

得分: 1

我认为问题出在你从 store 返回的部分。

Stored 接受一个泛型参数,它期望扩展自 Storable,但在 store 中当你返回 Stored&lt;T&gt; 时,实际上你返回的是 Stored&lt;MyObject&gt;,而 MyObject 并没有扩展自 Storable,这就是为什么 stored 的类型是 never 的原因。

所以,你最简单的解决方案是将返回类型更改为 Stored&lt;Storable&lt;T, B&gt;&gt;

英文:

I think that the problem is what you are returning from store.

Stored takes a generic parameter that is expecting to extends Storable, but when in store you are returning Stored&lt;T&gt;, you are actually returning Stored&lt;MyObject&gt;, and MyObject doesn't extends Storable, that is why stored is of type never.

So your simplest solution is to change the return type to Stored&lt;Storable&lt;T, B&gt;&gt;

huangapple
  • 本文由 发表于 2023年7月13日 14:52:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76676638.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定