Это реальный (упрощенный) пример некоторой функции lab
const lab = <
T extends Something,
K extends keyof T
>(k: T[K] extends (...args: any[]) => any ? K : never) => {
return ({} as T)[k];
}
const test = lab<Something, 'fn'>('fn')
type Something = {
str: string,
num: number
bool: boolean,
fn: (a: number, b: number) => number,
}
Это работает так, как ожидалось. Но на скринах ниже я показываю то, что тоже хочу и не понимаю, почему это невозможно.
разрешить str
в общем заполнителе в качестве ключа Something
, но ошибка с аргументом... так что это работает
событие, если аргумент становится str
также ошибка... так что работает
разрешите fn
и дайте чаевые в качестве аргумента... хорошо!
да, это только один правильный вариант и никаких ошибок! работает
fn
сразу? Или хотя бы любую из клавиш Something
, то есть str
, num
...?(см. предыдущий комментарий). Как только вы это исправите: TS просто не создает списки завершения для аргументов универсального типа при вызове универсальной функции. Эта ссылка на игровую площадку показывает то, что я считаю настоящим минимально воспроизводимым примером здесь. Я бы сказал, что если вас это действительно волнует, вы можете подать запрос на функцию TS. Или, может быть, вы можете выполнить рефакторинг для использования каррирования, как вот так. Это полностью решает вопрос? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?
@jcalz, Спасибо за совет! Кроме того, ваш пример более крутой, потому что он еще проще, но да — меня интересует why typescript don't tip me correct answers if it can say that another ones are incorrect?
. и ваш ответ: TS just doesn't seem to do completion lists for generic type arguments when calling a generic function
Звучит правильно... но для меня всё равно немного запутанно) P.S. может быть позже я постараюсь упростить и свой вопрос, изображения и т. д. В любом случае спасибо!)
Думаю, напишу ответ, как только появится возможность
Вы ищете IntelliSense, чтобы автоматически предлагать список завершения для аргумента общего типа для параметра типа K
при вызове функции lab()
. Но поддержка IntelliSense в TypeScript не обеспечивает таких дополнений. Вы можете показать это на минимальном примере, например:
const f = <K extends "x" | "y">() => 0;
f<"x">() // okay
f<"">()
// ^ abc "A"
// ^ abc "a"
// ^ abc "All"
// ^ abc "all"
// ...
Здесь IntelliSense должно быть невероятно легко рекомендовать либо "x"
, либо "y"
, но вместо этого вы получаете полный список всех строковых литералов, о которых знает TypeScript.
Существует давний запрос на открытие функции по адресу microsoft/TypeScript#28662 с просьбой об этом. Если вы хотите увидеть, как это произойдет, возможно, вам захочется пойти туда и поставить 👍. Но до тех пор, пока оно не будет реализовано, вам придется отказаться от него или обойти его.
Если вы готовы использовать значения вместо типов, вы можете каррировать свою функцию, чтобы указать аргумент типа для T
вручную, а затем возвращаемая функция будет ограничивать K
только теми типами, которые вы хотите поддерживать, и тогда вы не не указываем вручную K
; вместо этого вы просто вызываете функцию со значением типа K
и получаете список завершения IntelliSense для аргумента функции:
const lab = <T extends Something>() =>
<K extends { [P in keyof T]:
T[P] extends (...args: any) => any ? P : never
}[keyof T]>(
k: K
) => { return ({} as T)[k]; }
const test = lab<Something>()("") // <--
// ^🔧 fn
Здесь вы указали Something
, а затем получаете fn
в качестве предлагаемого ввода для возвращаемой функции.
Каррирование необходимо только в том случае, если вам вообще требуется K
быть универсальным; вы можете не:
const lab = <T extends Something>(k: { [P in keyof T]:
T[P] extends (...args: any) => any ? P : never
}[keyof T]) => { return ({} as T)[k]; }
const test = lab<Something>("") // <--
// ^🔧 fn
Но это небольшое отступление от ответа на заданный вопрос: тот тип списка завершения, который вам нужен, невозможен без microsoft/TypeScript#28662.
Ссылка на код детской площадки
Спасибо большое за такой развернутый ответ, @jcalz! Для меня это очень полезно как с точки зрения машинописи (даже далеко за рамками вопроса), так и в целом как пример хорошего дизайна общения, размещения вопросов и ответов и т. д. Я действительно вдохновлен, еще раз спасибо!)
Скриншоты IDE здесь, к сожалению, не уместны. Пожалуйста, замените изображения кода/журналов/ошибок/подсказок текстовыми версиями.