为什么 Partial<(param1: ParamType, ...) => ReturnType> 表现得像 any?

Why Partial<(param1: ParamType, ...) => ReturnType> behaves like any?

为什么这段代码可以编译?

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;

Playground Link

DeepPartial 应用于具有方法的类型时会出现此问题。

简答:Partial<(a: string) => number> 计算结果为 {},空对象类型,类似于 unknown,但排除了 nullundefined

更长的答案:

  • Partial<T> 是定义为 {[K in keyof T]?: T[K]}mapped type。它遍历 T 的属性并使它们中的每一个都是可选的。但是,在这种情况下,keyof ((a: string)=>number)never;它没有属性。 (好吧,函数类型 do 具有来自 Function 接口的属性,例如 bindcalllength 等,和 Object 接口的属性,例如 toStringvalueOf 等。但是这些属性通常对迭代没有用,所以它们被抑制了。)所以 Partial<(a: string)=>number> returns 没有属性的对象类型:空类型 {}.

  • 空类型 {} 的行为几乎与 unknown, in that nearly everything is assignable to it 类似。这是因为 TypeScript 中的对象类型是 "open",您可以向类型添加 属性 而不会使其不兼容。因此在

    interface Foo {a: string}
    interface Bar extends Foo {b: string}
    

    Bar 类型 ({a: string, b: string}) 的值也可以分配给 Foo 类型 ({a: string})。如果您得出其合乎逻辑的结论,那么任何对象类型都可以分配给空对象类型 {}。此外,如果 stringnumber 等基本类型的属性兼容,则认为它们可分配给对象类型。由于 stringnumber 类型确实具有属性(如 lengthtoFixed 等),它们也可以分配给空对象类型。当您尝试从中读取属性时,只有 nullundefined 实际上会抛出运行时错误,因此这两个值不被视为可分配给 {}


如果你想做递归映射类型,你应该决定当你点击一个函数类型 属性 时你希望看到什么,然后使用 conditional type 来实现发生。显而易见的事情就是保持方法函数类型不变(尽管方法的 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;
}
*/

好的,希望对你有帮助;祝你好运!

Link to code