我在这里查看这个问题:
最上面的答案的 DeepPartial 类型定义如下:
type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
我测试过,这种类型按预期工作,但我无法完全理解其内部是如何工作的。
让我们以同一问题的界面为例:
interface Foobar {
foo: number;
bar: {
baz: boolean;
qux: string;
};
}
当P=bar时,很容易理解,递归地会进入
DeepPartial<{
baz: boolean;
qux: string;
}>
并使“baz”和“qux”键可选。
但是我不明白的是,递归对于原始类型是如何工作的? 就像
foo
键一样,DeepPartial<number>
与 number
有何相同?当T=数字时,
[P in keyof number]?: DeepPartial<number[P]>;
对我来说没有意义。
在我看来,DeepPartial 的实现应该是这样的:
type DeepPartial<T> = {
[P in keyof T]?: isPrimitive(T[P]) ? T[P] : DeepPartial<T[P]>;
};
但是原来的实现也有效,但我不明白如何实现。
我希望我能很好地解释我的问题。这是游乐场示例:
编辑: 我进一步调查后发现
type DeepPartial<T> = {
[P in keyof T]?: any;
};
type DeepPartialTest<T> = {
[P in keyof number]?: any;
};
现在
DeepPartial<number>
将返回 number 类型,但 DeepPartialTest<number>
不会。这让我觉得更奇怪了。
编辑2添加截图:
这按预期工作:
这不是:
实际上两者应该是相同的?
我认为问题是“为什么作用于原语的映射类型会产生相同的原语,以及为什么它似乎只是有时发生”,而不是关于
DeepPartial
本身,对吗?
映射类型有两种“风格”。有些法线映射类型只是迭代属性键文字的任意联合(例如,
{[P in K]: ...}
),而同态映射类型专门迭代另一种类型的键(例如,{[P in keyof T]: ...}
),并尝试通过将 readonly
和
?
等修饰符从输入类型复制到输出类型来保留该类型的结构(除非您使用自己的
readonly
或 ?
和 +
更改修饰符)或 -
)。示例:
interface Obj {
a?: number,
b: string,
readonly c: boolean
}
type Homomorphic<T> = {
[K in keyof T]: Array<T[K]>
}
type ObjHom = Homomorphic<Obj>
/* type ObjHom = {
a?: (number | undefined)[] | undefined;
b: string[];
readonly c: boolean[];
} */
type ObjKeys = keyof Obj;
type ObjNonHom = {
[K in ObjKeys]: Array<Obj[K]>
}
/* type ObjNonHom = {
a: (number | undefined)[];
b: string[];
c: boolean[];
} */
在上面的代码中,
ObjHom
和ObjNonHom
类似,但是ObjHom
的a
属性是可选的,并且它的c
属性是readonly
(就像Obj
中一样);而 ObjNonHom
的属性都是必需的且可变的。
能够确定特定映射类型何时同态可能很棘手,因为涉及很多启发式方法。一般来说,具有
in keyof T
特征的映射类型(其中 T
是依赖于未解析的泛型参数的某种类型)将是同态的。一般来说,具有 in X
的映射类型(其中 X
是不直接包含 keyof
的类型)将是非同态的。当映射类型特征in keyof T
(其中T
)是某种非泛型类型时,事情就变得不确定了。对于像 number
这样的基元,这往往是非同态的,但对于对象类型,它往往是同态的。我可能可以浏览 GitHub 并找到实现每个启发式的 PR,但我不知道这是否值得任何人花时间。
原语的特定行为已在 microsoft/TypeScript#12447 中介绍。在描述中,它说:
形式的映射类型(其中{ [P in keyof T]: X }
是某个类型参数)被称为[同态]映射类型,因为它生成与T
具有相同形状的类型。 [...][当]在同构映射类型中用原始类型替换T
时,我们只需生成该原始类型即可。例如,当T
被实例化为 T 的{ [P in keyof T]: X }
时,我们会生成A | undefined
。{ [P in keyof A]: X } | undefined
所以这个问题的基本答案是:当映射类型是同态的并且作用于原始类型时,会产生相同的原始类型。原来的
DeepPartial
是同态的,所以number
变成了number
。直接使用 in keyof number
的修改版本是非同态的,因此 number
成为一种类型,其中 number
的每个明显属性都映射到可选属性。