为什么Partial <(param1:ParamType,...)=> ReturnType>的行为类似于任何行为?

时间:2019-10-31 13:02:49

标签: typescript type-level-computation

为什么这段代码会编译?

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应用于带有方法的类型时,就会出现此问题。

1 个答案:

答案 0 :(得分:2)

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

更长的答案:

  • Partial<T>是定义为{[K in keyof T]?: T[K]}的{​​{3}}。迭代T的属性,并使每个属性都是可选的。不过,在这种情况下,keyof ((a: string)=>number)never;它没有属性。 (好吧,函数类型 do 具有Function接口的属性,例如bindcalllength等,以及来自Object接口,例如toStringvalueOf等。但是这些属性通常对迭代无效,因此被抑制了。)因此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})。如果将其作为逻辑结论,则任何对象类型都可以分配给空对象类型{}。此外,如果stringnumber之类的原始类型的属性兼容,则认为它们可以分配给对象类型。由于stringnumber类型确实具有属性(例如lengthtoFixed等),因此它们也可以分配给空对象类型。当您尝试从其中读取属性时,只有nullundefined实际上会抛出运行时错误,因此这两个值不被认为可分配给{}


如果您要进行递归映射类型,则应确定当您击中函数类型的属性时想要看到什么,然后使用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;
}
*/

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

conditional type