#typescript
#typescript
Вопрос:
У меня есть функция, которая должна возвращать разные объекты в зависимости от параметра функции. Есть ли способ, как я могу добиться этого в Typescript?
Вот как далеко я продвинулся:
type returnMap = {
'a': number,
'b': string[]
}
function func<T extends returnMap, K extends keyof T>(val: K): T[K] {
if (val == 'a') {return 5}
if (val == 'b') {return ['a', 'b']}
return 42
}
console.log(func('a')) // 5
Однако в возвращаемых значениях говорится: «TS2322: тип ‘5’ не может быть присвоен типу ‘T[K]'».
Заранее благодарю
Ответ №1:
Это сделает:
interface ReturnMap {
'a': number;
'b': string[];
}
function func<K extends keyof ReturnMap>(val: K): ReturnMap[K];
function func(val: keyof ReturnMap): ReturnMap[keyof ReturnMap] {
if (val === 'a') return 5;
if (val === 'b') return ['a', 'b'];
throw new Error();
}
const r1 = func('a'); // const r1: number
const r2 = func('b'); // const r2: string[]
// Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'.ts(2345)
const r3 = func('c'); // const r3: number | string[]
Обратите внимание на синтаксис перегрузки функции:
function func<K extends keyof ReturnMap>(val: K): ReturnMap[K];
function func(val: keyof ReturnMap): ReturnMap[keyof ReturnMap] { // ...
Если бы вы использовали только вторую подпись, то возвращаемый тип был бы всегда number | string[]
.
Если вы использовали только первую подпись, то ReturnMap[K]
вычисляется (внутри тела функции) как number amp; string[]
контравариантный, поэтому вы получаете ошибки, которые вы описали в своем вопросе выше.
В качестве альтернативы, если вы не хотите использовать перегруженную сигнатуру — вы можете привести результат внутри тела функции:
function func<K extends keyof ReturnMap>(val: K): ReturnMap[K] {
if (val === 'a') return 5 as ReturnMap[K];
if (val === 'b') return ['a', 'b'] as ReturnMap[K];
throw new Error();
}
Комментарии:
1. Большое вам спасибо, Томаш. Это действительно работает! Не знал, что существует что-то вроде перегрузки функций или что это может решить мою проблему.
2. Привет @Tomasz Gawel, я не могу воспроизвести
If you only used first signature, then ReturnMap[K] is evaluated as number amp; string[]
. Не могли бы вы, пожалуйста, сказать мне, как это воспроизвести?3. @captain-yossarian я имел в виду внутри тела функции.
4. Для меня это не выглядит доказательством будущего. Хотя это работает для OP. Из вопроса кажется, что OP хочет иметь возвращаемый тип, который соответствует
K extends T[keyof T]
whereT extends ReturnValue
. таким образом, возвращаемый тип может быть не толькоnumber | string[]
, но он также должен содержать все типы значений вT extends ReturnValue
5. @Nishant При таком подходе вы могли бы написать общую подпись для любого типа (я имею в виду «return map», передаваемую как общий параметр), но поскольку это реализация функции, вы не могли бы предоставить фактическую реализацию для любого типа ;).
Ответ №2:
Я думаю, что перегрузка может вам помочь:
type returnMap = {
'a': number,
'b': string[],
'x': 42,
'z': 42,
}
function func<T extends returnMap, K extends Exclude<keyof returnMap,'a'|'b'>>(val: K): 42;
function func<T extends returnMap, K extends 'b'>(val: K): string[];
function func<T extends returnMap, K extends 'a'>(val: K): number;
function func<T extends returnMap, K extends keyof T>(val: K): number | string[] {
if (val == 'a') {
return 5
}
if (val == 'b') {
return ['a', 'b']
}
return 42
}
const result = func('a') // number
const result1 = func('b') // string[]
const result2 = func('x') // 42
const result3 = func('z') // 42
Комментарии:
1. Это тоже работает, но кажется немного более сложным, чем решение Tomasz. Однако для этого может быть причина (?)
2. Решение @Nayaro Tomasz немного лучше, потому что оно более общее, чем мое. Не стесняйтесь использовать его решение. Просто убедитесь, что вы знаете о перегрузках функций typescriptlang.org/docs/handbook/functions.html#overloads
Ответ №3:
Это кажется хорошим вариантом использования для использования any
в качестве типа возвращаемой переменной.
См. https://www.typescriptlang.org/docs/handbook/basic-types.html#any .
Комментарии:
1. Я думаю, что мыслительный процесс в вопросе
T[K]
заключаетсяnumber | string[]
в том, что. Итак, почему компилятор жалуется, когда код возвращаетсяnumber | string[]
.2. Это мое текущее решение для решения этой проблемы. Тем не менее, я хотел бы обеспечить больше удобства и безопасности типов. Вариант использования заключается в том, что этот метод выполняет некоторые вызовы API и возвращает разные объекты в зависимости от URL, который здесь указан как val .
3.
any
считается плохой практикой. Есть только несколько случаев, когда это может помочь, и я ничего о них не знаю)