2 votes

Types littéraux de modèles modifiés

const lines = ['one', 'two', 'three'] as const;
const linesWithA = lines.map(line => `${line}-A` as const);
const linesWithB = lines.map(line => `${line.toUpperCase()}-B` as const);

donnera des types :

declare const lines: readonly ["one", "two", "three"];
declare const linesWithA: ("one-A" | "two-A" | "three-A")[];
declare const linesWithB: `${string}-B`[];

Est-il possible d'obtenir un type pour linesWithB sous forme de ("ONE-B" | "TWO-B" | "THREE-B")[] ? Je reçois `${string}-A`[] au lieu de cela, à cause de la toUpperCase appeler.

TS Playground

3voto

T.J. Crowder Points 285826

Je ne peux le faire qu'avec une assertion de type (assez inoffensive) :

const lines = ['one', 'two', 'three'] as const;
type Lines = typeof lines;
type UpperLines = Uppercase<Lines[number]>[];
const linesWithA = lines.map(line => `${line}-A` as const);
const linesWithB = (lines.map(line => line.toUpperCase()) as UpperLines).map(line => `${line}-B` as const);

Lien vers le terrain de jeux

C'est également possible sans les alias de type intermédiaire, mais... je ne le ferais pas :

const lines = ['one', 'two', 'three'] as const;
const linesWithA = lines.map(line => `${line}-A` as const);
const linesWithB = (lines.map(line => line.toUpperCase()) as [Uppercase<(typeof lines)[number]>]).map(line => `${line}-B` as const);

Lien vers le terrain de jeux

J'espère que quelqu'un de meilleur en TypeScript que moi pourra le faire.

3voto

Oblosys Points 5035

Le problème est que le toUpperCase renvoie simplement un string :

const test1 = 'no uppercase'.toUpperCase();
// const test1: string

Je pense que la manière la plus sûre de gérer ce problème est de créer une nouvelle fonction toUpperCase avec une assertion pour obtenir le bon type :

const toUpperCase = <S extends string>(line: S) =>
  line.toUpperCase() as Uppercase<S>

const test2 = toUpperCase('uppercase');
// const test2: "UPPERCASE"

Si vous utilisez cette fonction dans linesWithB il aura le type de retour souhaité :

const linesWithB = lines.map(line => `${toUpperCase(line)}-B` as const);
// const linesWithB: ("ONE-B" | "TWO-B" | "THREE-B")[]

Lien vers le terrain de jeux

Mise à jour : Il y a un Problème de TypeScript à ce sujet, mais cela nécessitera (probablement ?) de faire String ce qui constitue un changement assez important.

1 votes

Je préfère de loin cette solution à celle qui consiste à augmenter les types globaux, bien qu'elle nécessite toujours une assertion de type comme la mienne.

1 votes

@T.J.Crowder Je pense qu'une affirmation est inévitable à moins que github.com/microsoft/TypeScript/issues/44268 est mis en œuvre.

0 votes

Je pense que le consensus était que Chaîne n'aurait pas besoin d'être générique, il suffirait de faire de la les méthodes individuelles génériques par rapport au type de la this -argument.

2voto

cherryblossom Points 5523

Vous pourriez augmenter l'espace de noms global et ajouter une surcharge pour la fonction toUpperCase fonction :

declare global {
    interface String {
        toUpperCase<T extends string>(this: T): Uppercase<T>
    }
}

Aucune assertion de type n'est requise :

// ("ONE-B" | "TWO-B" | "THREE-B")[]
const linesWithB = lines.map(line => `${line.toUpperCase()}-B` as const)

Lien vers l'aire de jeux

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X