Hvordan korrekt skrive returverdier i barneklasse (typescript)
Jeg vil lage en baseklasse for å jobbe med API (la oss kalle den BaseAPI). Deretter, for hvert API, lage en wrapper som UsersAPI, LoginAPI og lignende. Så baseklassen innkapsler bare logikken for å gjøre HTTP-forespørsler og tilpasse responser til samme struktur.
Minimalt eksempel:
type BaseResp = Record<string, unknown>
class BaseAPI {
async get(url: string): Promise<BaseResp> {
const resp = await fetch(url)
return await resp.json()
}
}
interface LoginResp extends BaseResp {
username: string
}
class AuthAPI extends BaseAPI {
async login(): Promise<LoginResp> {
return await this.get("/login")
}
}
Problemet er at BaseApi.get faktisk returnerer en Promise<any>, fordi jeg ikke vet hvilke data som kommer fra backend, så det er any.
Men jeg vet hvordan serveren svarer på /login API-kall, så jeg vil motta et typet objekt (LoginResp) etter å ha ventet på AuthAPI.login.
Min linter er satt opp på en måte som ikke tillater bruk av any, og jeg vil ikke undertrykke det. Derfor prøvde jeg å følge TS-konvensjoner og erstatte any med Record<string, unknown>.
Nå får jeg en feil: Property 'username' mangler i typen 'Promise
Hvordan sette opp skriving på riktig måte?
Svar #1
Siden resultatet av json råparsing alltid vil være any (typen kan ikke bestemmes ved kompileringstid), har du 2 valg.
1. Du stoler på det som kommer tilbake fra serveren.
I dette tilfellet kan du kaste de analyserte json-dataene, du kan endre din BaseApi-type til noe slikt:
class BaseAPI {
async get<T extends BaseResp>(url: string): Promise<T> {
const resp = await fetch(url)
return await resp.json() as T
}
}
2. Du stoler ikke på det som kommer tilbake fra serveren
I dette tilfellet kunne du sette opp typevakter:
function isLoginResp(input: any): input is LoginResp {
return typeof input === 'object' && typeof input.username === 'string'
}
Og mens du beholder resten av koden din som den er, kan du endre AuthApi-klassen din:
class AuthAPI extends BaseAPI {
async login(): Promise<LoginResp> {
const response = await this.get("/login")
if (isLoginResp(response)) {
return response
}
throw new TypeError(`Expected LoginResp, got ${JSON.stringify(response)}`)
}
}
