Hvordan bruke Typescript-funksjonsoverbelastning med en funksjon som har flere parametere?
Jeg har en funksjon som prøver å finne en verdi i en matrise av tall. Verdien kan enten være et objekt eller et tall. Hvis verdien er et objekt, finnes det en 'key'-egenskap som brukes til å hente tallverdien fra objektet.
Jeg prøver å bruke Funksjonsoverlasting for å ha en enkelt funksjon som kan håndtere begge tilfellene.
type ObjectType = {
[key: string]: number
}
type FunctionType = {
<T extends ObjectType>(v: T, list: number[], key: string): T | undefined
(v: number, list: number[]): number | undefined
}
const find: FunctionType = <T extends ObjectType | number,>(v: T, list: number[], key?: string)=>{
const value = typeof v === 'number' ? v : v[key]
return list.find((item)=>{
return item === value
})
}
Dette skaper en feil Type undefined kan ikke brukes som en indekstype
Men i denne grenen er verdien av v et objekt, så hvordan kan ikke nøkkelen være definert?
TS-lekeplass
Takk
Rediger
En løsning ble foreslått av Alex Wayne, men den begrenser ikke verdien til et tall på riktig måte.
Hvis vi i stedet for å kalle list.find, kalte en egendefinert funksjon som bare godtar tall, ville TypeScript kaste en feil
const findNumber = (v: number, list: number[])=>{
/* Do stuff with v as a number */
return list.indexOf(v)
}
const find: FunctionType = <T extends ObjectType | number>(v: T, list: number[], key?: keyof T)=>{
const value = (key && typeof v === 'object') ? v[key] : v
return list[findNumber(value, list)]
}
Type 'ObjectType' kan ikke tildeles typen 'number'
TS-lekeplass
Svar #1
Implementeringsfunksjonen har ingen anelse om hva dens overbelastninger er. Dette betyr at en overbelastningsfunksjons implementering fortsatt må være fullstendig gyldig hvis overbelastningsfunksjonens signaturer fjernes.
Så selv om det er sant at hvis typeof v === 'number' så typeof key === 'string', så vet ikke kompilatoren det.
Hvis du ser rent på denne funksjonssignaturen:
<T extends ObjectType | number,>(v: T, list: number[], key?: string)=>{
Da ser du at find({ someKey: 1 }, [1,2,3]) er en gyldig kall. Overlastene forhindrer den kallet, men igjen må denne funksjonen være gyldig uten de overlastene.
Som betyr at for å fikse dette må du teste for tilstedeværelsen av key før du bruker det.
const value = (key && typeof v === 'number') ? v : v[key]
Dette gir imidlertid et nytt problem:
Type 'undefined' cannot be used as an index type.(2538)
Jeg tror dette er fordi typeof v === 'number' ikke er en tilstrekkelig presisering. Jeg er ikke helt sikker på hva det ellers tror det kan være, men hvis du snur ternaryen og i stedet presiserer med typeof v === 'object' blir TypeScript fornøyd.
const value = (key && typeof v === 'object') ? v[key] : v
Lekeplass
Til slutt kan du sannsynligvis forbedre typesikkerheten her ved å bruke key: keyof T i stedet.
const find: FunctionType = <T extends ObjectType | number>(v: T, list: number[], key?: keyof T)=>{
const value = (key && typeof v === 'object') ? v[key] : v
return list.find((item)=>{
return item === value
})
}
Se lekeplass
Svar #2
Først av alt, la oss sørge for at ugyldig tilstand ikke kan representeres. La oss definere unionstyper:
type IsObj<T> = {
value: T, key: keyof T
}
type IsNumber = { value: number }
type Params<T> = IsObj<T> | IsNumber
Som du kanskje har lagt merke til, har jeg bundet 2. og 3. argument.
Nå kan vi definere vår egendefinerte typebeskytter:
const isNumber = <T,>(params: Params<T>): params is IsNumber => typeof params.value === 'number'
La oss sette alt sammen:
type ObjectType = {
[key: string]: number
}
type IsObj<T> = {
value: T, key: keyof T
}
type IsNumber = { value: number }
type Params<T> = IsObj<T> | IsNumber
const isNumber = <T,>(params: Params<T>): params is IsNumber => typeof params.value === 'number'
type FunctionType = {
<T extends ObjectType>(list: number[], params: IsObj<T>): T | undefined
(list: number[], params: IsNumber): number | undefined
}
const find: FunctionType = <T extends ObjectType | number,>(list: number[], params: Params<T>) => {
const value = isNumber(params) ? params.value : params.value[params.key]
return list.find((item) => item === value)
}
const result = find([1, 2, 3], { value: 42}) // ok
const result_ = find([1, 2, 3], { value: { age: 2 }, key: 'age' }) // ok
const result__ = find([1, 2, 3], { value: { age: 2 }, key: 'name' }) // expected error
const result___ = find([1, 2, 3], { value: 42, key: 'name' }) // expected error
Lekeplass
