Почему функция расширяет запись в TypeScript? - PullRequest
0 голосов
/ 28 июня 2018
const fn = () => null
type Answer = typeof fn extends Record<string, any> ? true : false

Приведенный выше фрагмент кода Answer будет иметь тип true, указывая, что функция считается подтипом Record. Почему?

Как я могу сделать свои типы более строгими, чтобы функции не были его подтипом?

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Итак, согласно вашему комментарию, вы хотите что-то вроде «объекта, который не является функцией». TypeScript в настоящее время не имеет отрицательных типов , поэтому нет краткого способа выразить «не функция». В зависимости от вашего варианта использования, вы можете просто пожать плечами и двигаться дальше.

Или вы можете попытаться заставить TypeScript склониться к вашей воле с помощью обходного пути.


Самый простой обходной путь - найти какое-либо свойство, которое всегда есть у функций, и объявить, что ваш тип имеет несовместимое необязательное свойство с тем же именем. Например:

type NoCall = { call?: number | string | boolean | null | undefined, [k: string]: any };
const okay: NoCall = { a: 4 }; // okay
const err: NoCall = () => 2; // error

Недостатком является то, что вы потеряете возможность проверки некоторых допустимых не-функций (в приведенном выше случае {call: ()=>0} не является функцией, но не сможет подтвердить как NoCall). Но, может быть, некоторые ложные срабатывания подходят для вашего случая использования?

В любом случае, в соответствии со стандартной библиотекой lib.es5.d.ts разумные варианты для имен свойств для проверки: apply, call, bind, arguments и caller.


Если вам не нравится отказываться от одного из этих имен, вы можете дополнительно использовать global augmentation , чтобы добавить фантомное свойство к интерфейсу Function:

declare global {
  interface Function {
    '*FunctionsHaveThis*': true
  }
}
type ObjNotFunction = { '*FunctionsHaveThis*'?: never, [k: string]: any };
const okay: ObjNotFunction = { a: 4 }; // okay
const err: ObjNotFunction = () => 2; // error

, что снижает вероятность конфликтов имен свойств, но загрязняет глобальное пространство имен и делает этот обходной путь значительно менее простым.


Другой обходной путь - использовать трюк с условными типами , которые предотвращают передачу функции в качестве параметра другой функции:

function noFunctionsPlease<T extends object>(t: T & (T extends Function ? never : T)) {
  // do something with t
}
noFunctionsPlease({ a: 4 }); // okay
noFunctionsPlease(() => 2); // error

Тип t более или менее точно говорит «объект, который не является функцией», но он может быть выражен только как аргумент функции.


Надеюсь, что один из них поможет вам или даст вам представление о том, как (или следует) действовать. Удачи!

0 голосов
/ 28 июня 2018

Record<string, any> указывает на любой объект, который имеет любой строковый ключ и любое значение. Все функции являются объектами, и у них есть некоторые свойства (мы даже можем индексировать объект fn['call']). Согласно этим определениям, и так как отношения типов в Typescript основаны на структуре, typeof fn расширяется Record<string, any>

...