为什么这段代码会编译?
const fn: Partial<(a: string) => number> = "LOL DIS IS A STRING"; // any non-null value
// Though this won't compile under strict null checks:
const fn: Partial<(a: string) => number> = null;
const fn: Partial<(a: string) => number> = undefined;
将DeepPartial
应用于带有方法的类型时,就会出现此问题。
答案 0 :(得分:2)
简短答案:Partial<(a: string) => number>
的计算结果为{}
,为空对象类型,类似于unknown
,其中排除了null
和undefined
。
更长的答案:
Partial<T>
是定义为{[K in keyof T]?: T[K]}
的{{3}}。迭代T
的属性,并使每个属性都是可选的。不过,在这种情况下,keyof ((a: string)=>number)
是never
;它没有属性。 (好吧,函数类型 do 具有Function
接口的属性,例如bind
,call
,length
等,以及来自Object
接口,例如toString
和valueOf
等。但是这些属性通常对迭代无效,因此被抑制了。)因此Partial<(a: string)=>number>
返回一个对象没有属性的类型:空类型{}
。
空类型{}
的行为几乎与mapped type相似,在其中unknown
中。这是因为TypeScript中的对象类型是“开放的”,您可以向类型添加属性而不会使其不兼容。因此在
interface Foo {a: string}
interface Bar extends Foo {b: string}
Bar
类型({a: string, b: string}
)的值也可分配给Foo
类型({a: string}
)。如果将其作为逻辑结论,则任何对象类型都可以分配给空对象类型{}
。此外,如果string
和number
之类的原始类型的属性兼容,则认为它们可以分配给对象类型。由于string
和number
类型确实具有属性(例如length
和toFixed
等),因此它们也可以分配给空对象类型。当您尝试从其中读取属性时,只有null
和undefined
实际上会抛出运行时错误,因此这两个值不被认为可分配给{}
。
如果您要进行递归映射类型,则应确定当您击中函数类型的属性时想要看到什么,然后使用nearly everything is assignable to it来实现。显而易见,要做的就是保持方法函数的类型不变(尽管方法的 presence 应该是可选的?不确定。)
所以看起来像
type DeepPartial2<T> = T extends Function ? T : {
[K in keyof T]?: DeepPartial2<T[K]>
}
interface Foo {
name: string,
age: number,
yell(): void,
friend: Foo
}
type DPFoo = DeepPartial2<Foo>;
/*
type DPFoo = {
name?: string | undefined;
age?: number | undefined;
yell?: (() => void) | undefined;
friend?: DPFoo | undefined;
}
*/
好的,希望能有所帮助;祝你好运!