Typescript:类型展开特定通用类型的属性

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

我想实现一种类型,它提取所有属性的内部类型是

Model<infer T>
,但不影响其他属性;像这样:

MyType<{ a: number, b: Model<string> }> // => { a: number, b: string }

我以为会这么简单:

type MyType<T> = {
  [P in keyof T]: T[P] extends Model<infer R> ? R : T[P];
};

但是当这样测试时:

const fnForTesting = <T>(obj: T): MyType<T> => {
  return null!;
};

const result = fnForTesting({
  name: "Ann",
  age: new Model<number>(),
});

const a = result.name; // unknown :( -> should be string
const b = result.age; // number -> works as expected

有人知道为什么“正常”属性没有被正确识别,而

Model
属性是吗?我该如何解决这个问题?提前致谢!

typescript typescript-typings typescript-generics type-constraints
2个回答
2
投票

你的实现只检查属性类型是否扩展模型,但如果没有,它只是返回原始类型而不做任何修改。

要解决此问题,您可以更新 MyType 类型以包含一个包罗万象的案例,该案例返回任何不扩展模型的属性的原始类型:

type MyType<T> = {
  [P in keyof T]: T[P] extends Model<infer R> ? R : T[P];
} & { [P in Exclude<keyof T, keyof Model<any>>]: T[P] };

这个更新的实现添加了第二个映射类型,其中包括所有不是模型的属性。 Exclude 表达式用于获取不属于模型的键。 & 运算符用于组合两个映射类型。


0
投票

由于您的模型类是空的,因此

Model<T>
{}
(空对象类型)相同。因此,当您使用
T[P] extends Model<infer R>
时,TypeScript 无法正确推断出
R
,因此它使用
unknown
。 TypeScript 不回退并回退到
T[P]
的原因是因为 所有类型都可以分配
{}
除了
null
undefined
。基本上,

type T = string extends {} ? true : false;
//   ^? true

现在请注意,当我向您的模型类添加属性以使其非空时,您的原始代码有效:

class Model<T>{
    value!: T;

    constructor() { }
}

// ...

const a = result.name; // string
//    ^?
const b = result.age; // number 
//    ^?
const c = result.date; // date 
//    ^?

这是因为现在字符串(或日期)和

Model<T>
之间存在明显的结构差异,TypeScript 现在可以检查
T[P]
是否为模型并推断类型。

游乐场(更改模型)


Filly 的代码有效,您实际上是在检查空对象是否可分配给无效的字符串(或日期)。在尝试推断模型的内部类型之前,这起到了保护作用。

type T = {} extends string ? true : false;
//   ^? false

这意味着

Model<unknown> extends T[P]
仅在
T[P]
是空对象或
Model<T>
时触发。

Playground(Filly 的解决方案)

(如果您的模型类在结构上与

{}
类型不同,则不需要 Filly 的解决方案)

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