Имея такой интерфейс, как:
// Mapping of argument types to return types
interface MyMapping {
a: { aResult: string }
b: { bResult: string }
}
Вы определенно можете сделать несколько магов c.
Сначала вам понадобится тип аргумента. Это будет каждый ключ из опционально включенного сопоставления со значением string
.
// Type of arguments. Each key in the mapping has a string value.
type UsesArgs<T> = { [K in keyof T]?: string }
Получить тип возврата немного сложнее. В основном вы хотите отобразить каждое свойство аргумента, а затем найти этот тип в отображении. Обратите внимание, что при выполнении итерации нам необходимо обеспечить, чтобы ключи имели тип keyof Args & keyof Mapping
, поскольку мы хотим быть уверены, что каждый ключ присутствует в обоих типах.
// Type of each possible return value, as a union of all types.
type ReturnValueTypeUnion<Args, Mapping> = {
// for each key Args and Mapping have in common, get the type from mapping
[K in keyof Args & keyof Mapping]: Mapping[K]
// Get values from mapping as a union
}[keyof Args & keyof Mapping]
Проблема заключается в том, что создается объединение { a: string } | { b : string }
, но то, что мы на самом деле хотим, это пересечение { a: string } & { b: string }
. К счастью, этот ответ имеет вспомогательный тип для этого.
// Convert a union to an intersection
intersection-type
type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: never
Теперь давайте создадим тип, чтобы собрать все это вместе:
// Type of the return values, as an intersection.
type ReturnValueType<Args, Mapping> =
UnionToIntersection<ReturnValueTypeUnion<Args, Mapping>>
И мы можем наконец, введите функцию:
declare function main<T extends UsesArgs<MyMapping>>(
opts: T,
): ReturnValueType<T, MyMapping>
// Examples
const a = main({ a: 'abc' }) // { aResult: string }
const b = main({ b: 'def' }) // { bResult: string }
const aPlusB = main({ a: 'abc', b: 'def' }) // { aResult: string } & { bResult: string }
Детская площадка