generics - Förhindra typutvidgning av objektliteral när de skickas som argument i TypeScript


Är det möjligt i den senaste TypeScript-versionen att skicka ett objektliteral som argument till en funktion utan att vidga det, och också utan att använda as const i anropet?

länk till TS Playground: Exempel

Det jag gör just nu är detta:

function func<T>(value: T): T { 
    return value;
};

let test = func({ key: 'value' })
// type inferred as { key: string;}

det jag vill ha är följande

// ... alternative declaration of func

let test = func({ key: 'value' })
// type inferred as { key: "value"; }

Mer exakt bör det fungera för vilket objektliteral som helst som utökar Record<string,string>

Dessa arkiverar resultatet jag vill ha, men jag skulle vilja att inte ändra sättet som funktionen måste anropas på

function func<T>(value: T): T {
    return value
};

let test = func({ key: 'value' as const })
// type inferred as { key: "value"; }

let test = func({ key: 'value' } as const )
// type inferred as { readonly key: "value"; }

Är detta möjligt?

Svar #1

Ja, detta är möjligt. Men lösningen kan verka ologisk och överflödig.

Du kommer att behöva lägga till en annan generisk typ till funktionen. Detta kommer att göra det möjligt för oss att behålla den avgränsade typen av strängliteraler som skickas till funktionen.

function func<T extends Record<string, S>, S extends string>(value: T): T { 
    return value;
};

let test = func({ key: 'value', a: "a" })
// let test: {
//     key: "value";
//     a: "a";
// }

Vi kan tillämpa detta på ditt komplexa exempel.

declare function formatMessage<
  Key extends keyof typeof messages, 
  Props extends { [key: string]: S }, 
  S extends string
>(messageKey: Key, props?: Props)
    :ReplaceValues<(typeof messages)[Key],  NonNullable<typeof props>>;

let test4 = formatMessage("created", {name: "TestValue"})
// let test4: "TestValue was created successfully."

Lekplats


Här är några ytterligare resurser som hjälpte mig med problem som dessa tidigare.

  • Typescript: Typinferens på funktionsargument

  • Förslag: Const-kontexter för generisk typinferens - Detta inlägg beskriver ett sätt att begränsa objektliteralers form av godtycklig typ.

Svar #2

För alla andra som stöter på denna fråga: ts-toolbelt har flera verktyg som hjälper till med denna typ av typmanipulering. I detta fall är F.narrow exakt vad som behövs för att typen ska härledas utan utvidgning, utan användning av någon form av trick:

import { F } from 'ts-toolbelt'

function func<T>(value: F.Narrow<T>) { 
    return value;
};

Som också kommer i TypeScript 5.0 tjänar "const-modifieraren" exakt detta syfte att förhindra typutvidgning.