Typescript опускает общий параметр, когда он не нужен

Я создал универсальный тип функции с возвращаемым параметром, который полностью зависит от типов входных параметров, а универсальный параметр имеет предложение extends. Вот фиктивная версия:

type Foo = {}
type MyFunc <T extends Foo> = (a: T, b: T) => T[];

const bar: MyFunc<number> = (a: number, b: number) => [a,b];
const baz: MyFunc<boolean> = (a: boolean, b: boolean) => [a,b];

Я использую общий параметр, чтобы убедиться, что 1) a и b расширяют Foo и 2) a и b имеют один и тот же тип.

Проблема, с которой я сталкиваюсь, заключается в использовании MyFunc в качестве типа параметра. Мне не нужен точный тип для T; Мне нужно только убедиться, что ограничения параметров выполняются при его вызове. Например, я хотел бы сделать следующее:

const makeArray = (qux: MyFunc) => qux(1,2);

1 и 2 имеют один и тот же тип и оба расширяют Foo, поэтому я знаю, что вызов функции будет работать, и я верну number[]. Однако приведенное выше не компилируется. Типскрипт говорит:

Generic type 'MyFunc' requires 1 type argument(s)

Если я использую (qux: MyFunc<any>), то проверки типов вообще не происходит.

Есть ли какой-либо другой синтаксис, который я могу использовать, чтобы указать TypeScript проверять входные параметры, но не требовать указания точного типа заранее?

Как TypeScript должен знать, как проверять входные параметры, если он не знает точного типа?

vera. 29.11.2022 15:44

В месте вызова он может просто проверить, что a и b имеют один и тот же тип и что они оба реализуют Foo. Этого было бы достаточно, чтобы сделать его типобезопасным, верно? Или вы видите ситуацию, когда это было бы неразумно?

Nate Glenn 29.11.2022 15:51

Так вы действительно просто запрашиваете общие параметры по умолчанию? type MyFunc<T extends Foo = Foo>

vera. 29.11.2022 15:58

Вероятно, вы тоже захотите сделать makeArray универсальным и передать этот универсальный тип MyFunc как const makeArray = <T extends Foo>(qux: MyFunc<T>) => qux(1,2).

Tobias S. 29.11.2022 16:13

Это выглядит как путаница с областью действия параметра универсального типа. Возможно, вы ищете что-то вроде this, где тип qux на самом деле является общим. Если это не то, что вы ищете, не могли бы вы точно объяснить, что вы имеете в виду?

jcalz 29.11.2022 17:15

@jcalz Да, это именно то, что мне было нужно, спасибо! Просто пришлось переместить часть <...> справа от знака равенства, чтобы T указывалось во время вызова, а не во время определения. Если вы напишите это как ответ, я приму это.

Nate Glenn 30.11.2022 13:19
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
6
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, вы неправильно определили область действия параметра универсального типа. В TypeScript есть два разных (но связанных) «разновидности» дженериков. См. TypeScript, как создать псевдоним универсального типа для универсальной функции? и разница машинописи между размещением аргументов дженериков например.

Существуют универсальные типы, в которых параметр типа объявляется в самом имени типа, например

// type GenType<T> = ...T...

И есть общие функции, где параметр типа объявлен в сигнатуре вызова, например

// type GenFunc = <T>(...T...)=>...

Изначально у вас был MyFunc как универсальный тип, который оценивается как неуниверсальная функция.

// original
type MyFunc<T extends Foo> = (a: T, b: T) => T[];

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

// changed
type MyFunc = <T extends Foo>(a: T, b: T) => T[];

Итак, теперь вы можете создать функцию типа MyFunc, и она будет действовать как ваша старая MyFunc<T> для всех возможных T:

// actually generic
const foo: MyFunc = (a, b) => [a, b];

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

const bar = foo<number>; 
// const bar: (a: number, b: number) => number[]
const baz = foo<boolean>; 
// const baz: (a: boolean, b: boolean) => boolean[]

Но я не знаю, насколько это полезно.


В любом случае, теперь вы можете реализовать makeArray так, как хотите, где MyFunc не требует аргумента типа:

const makeArray = (qux: MyFunc) => qux(1, 2);
// const makeArray: (qux: MyFunc) => number[]

И смотрите, компилятор делает вывод, что возвращаемый тип makeArray — это number[], как и хотелось!

Площадка ссылка на код

Другие вопросы по теме