Typ "json" | undefined uppfylls inte av "json" i Angular HttpClient


Jag granskar dokumentationen för HttpClient, med fokus på get(...)-metoden. Jag har förberett följande exempel:

const headers: HttpHeaders = new HttpHeaders();
const observe: HttpObserve = null;
const params: HttpParams = new HttpParams();
const reportProgress = false;
const responseType = "json";
const withCredentials = true;

const options = {
  headers, observe, params,
  reportProgress, responseType, withCredentials
};

this.http.get(url, options)

Jag får ett felmeddelande som säger följande.

No overload matches this call.  
The last overload gave the following error.  
Argument of type '{ responseType: string; ... }'  
is not assignable to parameter of type '{ responseType?: "json" | undefined; ... }'.  
Types of property 'responseType' are incompatible.
Type 'string' is not assignable to type '"json" | undefined'.

Det är ganska uppenbart vad det rapporterade problemet är. Jag ser dock inte hur det jag skrev validerar mot det som krävs. Om jag skriver undefined som värde för responseType är kompilatorn nöjd. Faktum är att de utvecklade kodexemplen (nummer 7, 8 och 12 till 15) uttryckligen anger att det är den syntax som ska användas.

Hur är min "json" inte den nödvändiga "json"?

Svar #1

Metoderna i HttpClient använder strängliteraltyper för några av alternativen - istället för att bara deklarera t.ex. responseType som den generiska string anger de de specifika värden som den kan ta. Så varför uppfyller inte ditt options-objekt typdefinitionen, trots att det har ett av de accepterade värdena?

Den initiala deklarationen:

const responseType = "json";

definierar responseType som strängliteralt typ "json"; det är en const, det kan bara någonsin ha det enda värdet. Hittills, så bra. Men objekt-deklarationen:

const options = { responseType /* etc. */ };

ger options typen { responseType: string }, det vidgar typen av attributet. Det gör detta eftersom objekt är ändringsbara, så du kan ändra värdet.

För att åtgärda detta har du flera alternativ; i ingen särskild ordning:

  • Infoga objektet direkt:

     this.http.get(url, { responseType });
    

    Detta breddar inte typen, eftersom du inte kan tilldela ett annat värde till ett objekt som du inte har en referens till.

  • Skriv uttryckligen typen för det mellanliggande objektet:

     const options: { responseType: "json" } = { ... };
    
  • Använd en const-påstående på objektet:

     const options = { responseType } as const;  // or = <const>{...};
    

    Detta säger till kompilatorn att du inte kommer att ändra värdena och ger options typen { readonly responseType: "json" }.

  • Använd en const-assertion på strängen (föreslaget av Michael D):

    const responseType = "json" as const;  // or = <const>"json";
    

    Den här är lite konstig, eftersom typen av responseType fortfarande är "json", som den ursprungligen var. Dock skapar detta ett "const context" där den typen är icke-breddande, så den resulterande typen av options är { responseType: "json" } (inte readonly som ovan, men du kan bara tilldela det värdet till den).

Här är en lekplats som visar de olika alternativen för options: TypeScript Playground.

Relaterade artiklar