Chaînes de modèles étiquetées dactylographiées

Comment utiliser des chaînes de modèle en tant que fonctions

Les bases : chaînes de modèles

Avant de parler des chaînes de modèle étiquetées, également appelées littéraux de modèle, je veux juste donner une brève introduction aux chaînes de modèle en général. N'hésitez pas à passer au chapitre suivant si vous savez déjà de quoi il s'agit.

Les chaînes de modèle sont un type spécial de symboles de chaîne qui peuvent également contenir des expressions Javascript et s'étendre sur plusieurs lignes. Ils utilisent les caractères backtick au lieu des guillemets doubles, comme c'est le cas pour les chaînes courantes.

En ce qui concerne la répartition des caractères sur plusieurs lignes, une chaîne commune doit contenir une barre oblique inverse en combinaison avec le caractère « n » pour créer une nouvelle ligne. Avec les chaînes de modèle, vous pouvez simplement créer une nouvelle ligne « inline », pour ainsi dire.

// A common string as reference.
const string = "I'm just a string";

// A template string. For Typescript,
// this value is a plain 'string'-type.
const templateString = `I'm just a string as well`;

La disponibilité des expressions à l'intérieur d'une chaîne peut également être expliquée rapidement. Au lieu de simplement autoriser les symboles de chaîne, les chaînes de modèle acceptent des expressions arbitraires. L'exemple suivant montre ce que je veux dire par là.

function greet(name: string) {
  // Here you see an expression
  // *embedded* inside a string.
  return `Hello, ${name}!`;
}

// "Hello, Tom!"
const greeting = greet("Tom");

// One more example, using
// Typescript's 'rest'-operator
// which allowes any number of values
// and provides them as an array,
// in our case of type 'string'.
function greetAll(...names: string[]) {
  return `Hi everybody: ${names.join(", ")}!`;
}

// "Hi everybody: Tom, Luke!"
const greetingForAll = greetAll("Tom", "Luke");

Chaînes de modèle balisées

Cet ensemble de fonctionnalités à lui seul serait déjà bien, mais les chaînes de modèles peuvent également être utilisées comme fonctions. Vous mettez simplement un mot-clé devant la chaîne de modèle pour la « marquer », d'où le nom « chaînes de modèle taguées ». Il est temps pour l'exemple suivant.

// All previous examples used 'untagged'
// string literals, which means they're 
// practially just a string.
//
// But let's see how we can convert them
// to an acutal function:
function merge(template: TemplateStringsArray, ...params: string[]){
  
  // This needs some explanation:
  //
  // 'template', our first param, contains
  // all strings *inbetween the paramters*,
  // you'll see in a minute what I mean by that.
  //
  // 'params' then is an array of strings
  // that were provided as paramteres to the
  // template string.
  
  // Let's ignore the result for now.
  return "";
}

const what = "test";

// Here's the tagged template string in action.
// The tag is the function name, and like a 
// function the tagged template string can be called.
//
// Let's destruct how this will look like
// in the function defined above.
//
// 'template' = ["Just a", ""];
// 'params'   = ["test"]
const result = merge`Just a ${what}`;

// As you can see, the function splits the string
// into the string-only parts and the expressions.

Comme vous pouvez le voir, la syntaxe semble assez intéressante et peut-être même un peu étrangère lors de la première utilisation. Il n'y a probablement pas beaucoup de cas d'utilisation qui vous obligeraient à implémenter des chaînes de modèles étiquetées, mais nous pouvons néanmoins jouer avec. Dans le dernier exemple suivant, j'ai rassemblé quelques études de cas. Comme vous le verrez, les chaînes de modèles étiquetées peuvent naturellement être utilisées avec des génériques et ouvrir des options intéressantes pour la mise en œuvre de certaines exigences.

// 
// Generic
//
// Tagged template literals can be generic, too.
function generic<T>(template: TemplateStringsArray, ...params: T[]){
    return template.join(",") + params.join(",")
}

// "A value: ,test"
console.log(generic<string>`A value: ${"test"}`);

//
// Generic (with super powers)
//
// You can specify each type and 
// also limit the number of params!
function coalesce<A, B, C>(template: TemplateStringsArray, ...params: [A, B, C]){
    return template.join(",") + params.join(",")
}

// ", - , - ,value,0,true" 
const res = coalesce<string, number, boolean>`${"value"} - ${0} - ${true}`;

//
// Different return type
//
// Also, tagged literal types don't 
// only have to return strings.
const preview = (template: TemplateStringsArray, ...params: number[]) 
  => (template2: TemplateStringsArray, ...params2: string[])
  => {
    return "what?";
}

// Note the two pairs of backticks
// after each other, we're just calling
// the returned tagged template string
// form our first one!
console.log(preview`${0}``${"a"}`);

J'espère que vous avez apprécié cette excursion assez rapide dans l'une des fonctionnalités de Javascript et de Typescript. Les chaînes de modèles sont probablement une chose courante dans votre base de code, les chaînes de modèles étiquetées probablement pas, il est donc intéressant d'en savoir plus sur ces fonctionnalités de niche.

Suggestions

Connexe

Addenda

Langues