Почему Partial <(param1: ParamType, ...) => ReturnType> ведет себя как любой? - PullRequest
0 голосов
/ 31 октября 2019

Почему этот код компилируется?

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 Ответ

2 голосов
/ 31 октября 2019

Краткий ответ: Partial<(a: string) => number> оценивается как {}, пустой тип объекта, такой как unknown с null и undefined исключенным.

Более длинный ответ:

  • Partial<T> - это отображенный тип , определенный как {[K in keyof T]?: T[K]}. Он перебирает свойства T и делает каждое из них необязательным. В этом случае, однако, keyof ((a: string)=>number) есть never;у него нет свойств. (Ну, типы функций do имеют свойства интерфейса Function, такие как bind, call, length и т. Д., И свойства интерфейса Object, такие как toString и valueOf и т. Д. Но эти свойства обычно бесполезны для итерации, поэтому они подавляются.) Поэтому Partial<(a: string)=>number> возвращает тип объекта без свойств: пустой тип {}.

  • Пустой тип {} действует почти так же, как 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 фактически выдают ошибки времени выполнения при попытке прочитать из них свойства, поэтому эти два значения не считаются назначаемыми {}.


Если вы хотите создавать рекурсивные сопоставленные типы, вы должны решить, что вы хотели бы видеть, когда вы нажмете свойство с типом функции, а затем использовать условный тип , чтобы это произошло. Очевидная вещь, которую нужно сделать, это просто сохранить тип функции метода как есть (хотя, возможно, наличие метода должно быть необязательным? Не уверен.)

Так что это будет выглядеть как

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;
}
*/

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...