typescript - Typen 'Observable' kan inte tilldelas typen 'Observable'
Vi använder Knockout.js (v3.5.0) och dess TypeScript-definitioner. De fungerade okej fram till TypeScript 4.6.2. Problemen verkar dock vara "djupare" än i definitionsfilen. Det verkar som att det skett någon förändring i TypeScript i hur en boolean-typ hanteras. Så istället för att märka denna fråga som ett Knockout.js-problem, skapade jag ett litet kodexempel inspirerat av Knockout d.ts som illustrerar problemet:
interface Observable<T>
{
(): T;
(value: T): any;
}
function observable<T>(value: T): Observable<T>
{
return undefined as any; // the implementation is not important
}
const x: Observable<boolean> = observable(false);
Den här koden har ett kompileringsproblem:
Type 'Observable<false>' is not assignable to type 'Observable<boolean>'.
Types of parameters 'value' and 'value' are incompatible.
Type 'boolean' is not assignable to type 'false'.
Självklart fungerar det att casta false som boolean, men jag betraktar det som en hack, inte en lösning (uppenbarligen behöver vi casta varje förekomst av true/false). Finns det något sätt att faktiskt lösa detta?
Redigera: baserat på kommentaren är det uppenbart att viss typkontroll ändrades. Fler exempel kan ses här. Länk till Playground.
Finns det någon information (med förklaring) för denna förändring?
Redigera2: som föreslagits i kommentarerna, har jag skickat en buggrapport på https://github.com/microsoft/TypeScript/issues/48150
Svar #1
Jag skulle föreslå att istället uttryckligen specificera den generiska typen som:
const x = observable<boolean>(false);
Anledningen till detta är att vi antar att false innebär boolean. Vanligtvis gör det det, men inte alltid. Föreställ dig t.ex. dessa typer
const x = observable<false | undefined>(false);
const y = observable(false);
(För alla som försöker lösa skrivproblemet på något annat sätt, se till att ovanstående rader också är giltiga.)
Svar #2
Enligt användaren som har svarat på Github-ärendet (https://github.com/microsoft/TypeScript/issues/48150) är Typescript 4.6-kompileringsfelet väntat:
Jag tror att detta är ett korrekt fel som inte hanterades på rätt sätt i gamla versioner. Den generiska parametern T är invariant eftersom den används både i en kovariant position() => T och i en kontravariant position (value: T) => any.
vilket faktiskt är sant. Eftersom användaren hjälpte till att lösa problemet, för fullständighetens skull kommer jag att försöka omformulera och sammanfatta hans kommentarer här.
Den första föreslagna lösningen löser problemet endast delvis:
function observable<T>(value: T extends infer U ? U: never): Observable<T>
{
return undefined as any; // the implementation is not important
}
const x: Observable<boolean> = observable(false); // no error
men det kommer att producera unknown-typ utan en typanteckning.
const bad = observable(false);
// ^ unknown, which should be Observable<boolean>
Den korrekta lösningen verkar vara att införa en annan typparameter
declare function observable<T, U = any>(value?: T extends infer R ? R : U):
unknown extends T ? Observable<U> : Observable<T>
Lekplatslänk
Eftersom huvudproblemet var med Knockout.js-biblioteket, rapporterade jag problemet där (https://github.com/knockout/knockout/issues/2589), förhoppningsvis kommer någon med tillräcklig kunskap i Typescript att rätta problemet.
