Допустим, я хочу написать функцию, которая возвращает ключи либо из Map
, либо из простого объекта в виде простого массива, что-то вроде этого:
function keys <K>(m: Map<K, any> | { [key: string]: any }) {
if (m instanceof Map) return Array.from(m.keys())
return Object.keys(m)
}
Если затем я попытаюсь получить ключи от обычного объекта, это сработает нормально:
let a = ((g: string[]) => g)(keys({})) // this works
Однако использование Map<number, number>
дает мне ошибку типа:
let a = ((g: number[]) => g)(keys(new Map<number, number>()))
// Argument of type 'number[] | string[]' is not assignable to parameter of type 'number[]'
let a = ((g: number[]) => g)(keys<number>(new Map<number, number>()))
// Even this produces the same error
let a = ((g: number[]) => g)(keys(new Map<number, number>()) as number[])
// This works but the cast kind of defeats the purpose
Что мне не хватает? Как я должен ввести это, чтобы компилятор TypeScript понял, что я имею в виду?
Обновлять:
Перегрузка сигнатуры функций, кажется, здесь подходит, эта версия работает нормально:
function keys <K>(m: Map<K, any>): K[]
function keys (m: { [key: string]: any }): string[]
function keys (m: any): any {
if (m instanceof Map) return Array.from(m.keys())
return Object.keys(m)
}
Проверьте перегрузку подписи функции машинописного текста: https://www.typescriptlang.org/docs/handbook/functions.html#overloads
вы можете определить набор различных подписей, так что вы можете определить подпись для случая Map
и одну для случая object
:
function keys <K>(m: Map<K, any>): K[]
function keys (m: { [key: string]: any }): string[]
function keys (m: any): any {
if (m instanceof Map) return Array.from(m.keys())
return Object.keys(m)
}
Вы можете использовать простые функции с дженериками:
function keys<K extends PropertyKey>(m: Map<K, any> | { [P in K]: any }): K[] {
if (m instanceof Map) return Array.from(m.keys())
return Object.keys(m) as K[]
}
let a = (g => g)(keys({})) // never[]
let a0 = (g => g)(keys({foo: "bar"})) // "foo"[]
let a1 = (g => g)(keys(new Map<number, number>())) // number[]
let a2 = (g => g)(keys<number>(new Map<number, number>())) // number[]
let a3 = (g => g)(keys(new Map<1 | 2, number>())) // (1 | 2)[]