Er det mulig i TypeScript å definere en betinget returtype og ikke bruke typekonvertering i funksjonskroppen?


Jeg prøver å korrekt angi returtypen til en funksjon basert på inndataene, og så tvinger TypeScript meg til å bruke as-nøkkelordet for å løse problemet jeg har. Finnes det en måte å unngå dette på, og finnes det en elegant løsning?

type TestReturnType<T> = T extends string ? number : string;

const test = <T,>(value: T): TestReturnType<T> => {
    if (typeof value === 'string') {
        return 1 as TestReturnType<T>; // ?
    } else {
        return '1' as TestReturnType<T>; // ?
    } 
};

const res1 = test('String'); 
const res2 = test(13123);   

Svar #1

Du kan bruke funksjonsoverlasting for å oppnå dette:

type TestReturnType<T> = T extends string ? number : string;

function test(value: string): TestReturnType<string>;
function test(value: number): TestReturnType<number>;
function test<T>(value: T): TestReturnType<string | number> {
    if (typeof value === 'string') {
        return 1;
    } else {
        return '1';
    }
}

const res1 = test('String');   // res1: number
const res2 = test(13123);      // res2: string

Merk at,

  • hvis du utelater overbelastningene, vil både res1 og res2 ha typen string | number
  • du kan fjerne typeparameteren til test og erklære den som function test(value: any)

Svar #2

Nei, for øyeblikket når en funksjon returnerer en betinget type som avhenger av en generisk typeparameter, må du bruke en typepåstand (det du kaller "typecasting") i return-linjen, eller gjøre noe tilsvarende løst typet (for eksempel overbelastning av funksjonen). Det finnes dessverre ingen måte å få kompilatorverifisert typesikkerhet på. Se microsoft/TypeScript#33912 for den relevante funksjonsforespørselen.

Problemet er at selv om kontrollflytanalyse kan brukes til å snevre inn typene til verdier av generiske typer, påvirker det ikke den generiske typeparameteren i det hele tatt. Inne i din test()-funksjon kan typeof value === "string" snevre inn value fra T til T & string, men T i seg selv endres ikke for å bli begrenset til string. Dermed kan ikke typen T extends string ? number : string evalueres som number; det er bare T extends string ? number : string.

Relaterte artikler