IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

TypeScript Discussion :

TypeScript 4.1 apporte le remappage de clé dans les types mappés, les types template de littéraux


Sujet :

TypeScript

  1. #1
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Mars 2013
    Messages
    8 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Mars 2013
    Messages : 8 461
    Points : 197 892
    Points
    197 892
    Par défaut TypeScript 4.1 apporte le remappage de clé dans les types mappés, les types template de littéraux
    TypeScript 4.0 Beta permet aux tuples d'accepter un nombre variable de paramètres,
    et apporte l'inférence de propriété de classe des constructeurs

    Types de tuple variadique (qui accepte un nombre variable de paramètres)

    Considérez une fonction en JavaScript appelée concat, qui prend deux types de tableau ou de tuple et les concatène ensemble comme un nouveau tableau.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function concat(arr1, arr2) {
        return [...arr1, ...arr2];
    }

    Considérez également tail, qui prend un tableau ou un tuple, et renvoie tous les éléments sauf le premier.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function tail(arg) {
        const [_, ...result] = arg;
        return result
    }

    Comment pourrions-nous taper l'un de ces éléments dans TypeScript?

    Pour concat, la seule chose valable que nous pouvions faire dans les anciennes versions du langage était d'essayer d'écrire des surcharges. Une fonction surchargée est une fonction qui est définie plusieurs fois dans une application afin d’offrir différentes logiques à l’application.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function concat<>(arr1: [], arr2: []): [A];
    function concat<A>(arr1: [A], arr2: []): [A];
    function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
    function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
    function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
    function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
    function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];)

    Ce qui nous donne donc sept surcharges lorsque le deuxième tableau est toujours vide. Ajoutons-en pour quand arr2 a un argument.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function concat<A2>(arr1: [], arr2: [A2]): [A2];
    function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2];
    function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
    function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
    function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
    function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
    function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];

    Il est donc clair que cela devient déraisonnable. Malheureusement, vous vous retrouveriez également avec les mêmes types de problèmes lors de la saisie d'une fonction comme tail.

    C'est un autre cas de ce que l'équipe TypeScript aime appeler « la mort par mille surcharges », et cela ne résout même pas le problème en général. Il ne donne que les types corrects pour autant de surcharges que nous voulons écrire. Si nous voulions créer un cas fourre-tout, nous aurions besoin d'une surcharge comme celle-ci:

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    function concat<T, U>(arr1: T[], arr2, U[]): Array<T | U>;

    Mais cette signature n'encode rien à propos de la longueur de l'entrée ou l'ordre des éléments lors de l'utilisation de tuples.

    TypeScript 4.0 apporte deux changements fondamentaux, ainsi que des améliorations d'inférence, pour rendre leur saisie possible.

    Le premier changement est que les spreads (Spread ou REST paramètre est une technique qui peut être utilisée dans le cas où nous ne connaissons pas le nombre d'arguments qui sera envoyé à la fonction, un paramètre spread est dénoté par trois points (...) qui se placent devant le nom de paramètre) dans la syntaxe de type tuple peuvent désormais être génériques. Cela signifie que nous pouvons représenter des opérations d'ordre supérieur sur des tuples et des tableaux même lorsque nous ne connaissons pas les types réels sur lesquels nous opérons. Lorsque des spreads génériques sont instanciés (ou remplacés par un type réel) dans ces types de tuples, ils peuvent produire d'autres ensembles de types de tableau et de tuple.

    Par exemple, cela signifie que nous pouvons taper une fonction comme queue, sans notre problème de «mort par mille surcharges».

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function tail<T extends any[]>(arr: readonly [any, ...T]) {
        const [_ignored, ...rest] = arr;
        return rest;
    }
     
    const myTuple = [1, 2, 3, 4] as const;
    const myArray = ["hello", "world"];
     
    // type [2, 3, 4]
    const r1 = tail(myTuple);
     
    // type [2, 3, ...string[]]
    const r2 = tail([...myTuple, ...myArray] as const);

    Le deuxième changement est que les éléments spread peuvent apparaître n'importe où dans un tuple - pas seulement à la fin!

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Strings = [string, string];
    type Numbers = [number, number];
     
    // [string, string, number, number]
    type StrStrNumNum = [...Strings, ...Numbers];

    Auparavant, TypeScript générait une erreur comme celle-ci.

    Citation Envoyé par TypeScript
    A rest element must be last in a tuple type.
    Mais maintenant, le langage peut aplanir les spreads à n'importe quelle position.

    Lorsque nous répartissons un type sans longueur connue, le type résultant devient également illimité et tous les éléments consécutifs sont pris en compte dans le type d'élément de repos résultant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Strings = [string, string];
    type Numbers = number[]
     
    // [string, string, ...Array<number | boolean>]
    type Unbounded = [...Strings, ...Numbers, boolean];
    En combinant ces deux comportements ensemble, nous pouvons écrire une seule signature bien typée pour concat:

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Arr = readonly any[];
     
    function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
        return [...arr1, ...arr2];
    }

    Bien que cette signature soit encore un peu longue, il ne s'agit toujours que d'une signature, elle ne doit être écrite qu'une seule fois, et elle donne en fait un comportement prévisible sur tous les tableaux et tuples.

    Si l'équipe est plutôt satisfaite de cette fonctionnalité, elle reconnaît qu'il existe également d'autres scénarios plus sophistiqués. Par exemple, considérons une fonction pour appliquer partiellement des arguments appelés partialCall.partialCall qui prend une fonction avec les quelques arguments initiaux attendus par cette fonction. Elle renvoie ensuite une nouvelle fonction qui prend tous les autres arguments dont la fonction a besoin et les appelle ensemble.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function partialCall(f, ...headArgs) {
        return (...tailArgs) => f(...headArgs, ...tailArgs)
    }

    TypeScript 4.0 améliore le processus d'inférence pour les paramètres de repos et les éléments de tuple de repos.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Arr = readonly unknown[];
     
    function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) {
        return (...b: U) => f(...headArgs, ...b)
    }

    Dans ce cas, partialCall comprend les paramètres qu'il peut et ne peut pas prendre initialement et renvoie des fonctions qui acceptent et rejettent de manière appropriée tout ce qui reste.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const foo = (x: string, y: number, z: boolean) => {}
     
    // This doesn't work because we're feeding in the wrong type for 'x'.
    const f1 = partialCall(foo, 100);
    //                          ~~~
    // error! Argument of type 'number' is not assignable to parameter of type 'string'.
     
     
    // This doesn't work because we're passing in too many arguments.
    const f2 = partialCall(foo, "hello", 100, true, "oops")
    //                                              ~~~~~~
    // error! Expected 4 arguments, but got 5.
     
     
    // This works! It has the type '(y: number, z: boolean) => void'
    const f3 = partialCall(foo, "hello");
     
    // What can we do with f3 now?
     
    f3(123, true); // works!
     
    f3();
    // error! Expected 2 arguments, but got 0.
     
    f3(123, "hello");
    //      ~~~~~~~
    // error! Argument of type '"hello"' is not assignable to parameter of type 'boolean'.

    Nom : type.png
Affichages : 3604
Taille : 89,1 Ko

    Éléments de tuple étiquetés

    Il est important d'améliorer l'expérience des types de tuples et des listes de paramètres. L'idée que nous pouvons utiliser des types de tuples pour les paramètres de repos devient cruciale. Par exemple, la fonction suivante qui utilise un type de tuple comme paramètre de repos…

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function foo(...args: [string, number]): void {
        // ...
    }

    … Ne devrait pas différer de la fonction suivante…

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function foo(arg0: string, arg1: number): void {
        // ...
    }

    … Pour tout appelant de foo.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foo("hello", 42); // works
     
    foo("hello", 42, true); // error
    foo("hello"); // error

    Il y a un endroit où les différences commencent à devenir observables: la lisibilité. Dans le premier exemple, nous n'avons aucun nom de paramètre pour les premier et deuxième éléments. Bien que ceux-ci n'aient aucun impact sur la vérification de type, le manque d'étiquettes sur les positions des tuples peut les rendre plus difficiles à utiliser - et il devient donc plus difficile de communiquer sur notre intention.

    C’est pourquoi dans TypeScript 4.0, les types de tuples peuvent désormais fournir des étiquettes.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    type Range = [start: number, end: number];

    Poussant davantage la connexion entre les listes de paramètres et les types de tuples, l'équipe a fait en sorte que la syntaxe des éléments de repos et des éléments facultatifs reflète celle des listes de paramètres.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    type Foo = [first: number, second?: string, ...rest: any[]];

    Lors de l'étiquetage d'un élément de tuple, tous les autres éléments du tuple doivent également être étiquetés.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    type Bar = [first: string, number];
    //                         ~~~~~~
    // error! Tuple members must all have names or all not have names.

    Il convient de noter que les étiquettes ne nous obligent pas à nommer nos variables différemment lors de la déstructuration. Ils sont purement là pour la documentation et l'outillage.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function foo(x: [first: string, second: number]) {
        // ...
     
        // note: we didn't need to name these 'first' and 'second'
        let [a, b] = x;
     
        // ...
    }

    Inférence de propriété de classe des constructeurs

    TypeScript 4.0 peut désormais utiliser l'analyse de flux de contrôle pour déterminer les types de propriétés dans les classes lorsque noImplicitAny est activé.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Square {
        // Previously: implicit any!
        // Now: inferred to `number`!
        area;
        sideLength;
     
        constructor(sideLength: number) {
            this.sideLength = sideLength;
            this.area = sideLength ** 2;
        }
    }

    Dans les cas où tous les chemins d'un constructeur ne sont pas attribués à un membre d'instance, la propriété est considérée comme potentiellement undefined.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Square {
        sideLength;
     
        constructor(sideLength: number) {
            if (Math.random()) {
                this.sideLength = sideLength;
            }
        }
     
        get area() {
            return this.sideLength ** 2;
            //     ~~~~~~~~~~~~~~~
            // error! Object is possibly 'undefined'.
        }
    }

    Source : billet TypeScript
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

  2. #2
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Mars 2013
    Messages
    8 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Mars 2013
    Messages : 8 461
    Points : 197 892
    Points
    197 892
    Par défaut TypeScript 4.0 est disponible et apporte des améliorations de la vitesse dans le mode build
    TypeScript 4.0 est disponible et apporte des améliorations de la vitesse dans le mode build,
    ainsi que l'inférence de propriété de classe des constructeurs

    Microsoft a annoncé la disponibilité de TypeScript 4.0. En guise d'introduction, l'éditeur explique :

    « Cette version du langage représente notre prochaine génération de versions de TypeScript, alors que nous approfondissons l'expressivité, la productivité et l'évolutivité.

    « Si vous n'êtes pas familier avec TypeScript, c'est un langage qui s'appuie sur JavaScript en ajoutant une syntaxe pour les types statiques. L'idée est qu'en écrivant les types de vos valeurs et où elles sont utilisées, vous pouvez utiliser TypeScript pour vérifier le type de votre code et vous informer des erreurs avant d'exécuter votre code (et même avant d'enregistrer votre fichier). Vous pouvez ensuite utiliser le compilateur TypeScript pour supprimer les types de votre code et vous laisser avec un JavaScript propre et lisible qui s'exécute n'importe où. Au-delà de la vérification, TypeScript utilise également des types statiques pour alimenter d'excellents outils d'édition tels que l'autocomplétion, la navigation dans le code, les refactorisations, etc. En fait, si vous avez utilisé JavaScript dans un éditeur tel que Visual Studio Code ou Visual Studio, vous avez déjà utilisé une expérience optimisée par types et TypeScript.

    « Avec TypeScript 4.0, il n'y a pas de changements majeurs de rupture. En fait, si vous êtes nouveau dans le langage, c'est le meilleur moment pour commencer à l'utiliser. La communauté est déjà là et grandit, avec un code fonctionnel et de nouvelles ressources à apprendre. Et une chose à garder à l'esprit: malgré toutes les bonnes choses que nous apportons dans la version 4.0, il vous suffit de connaître les bases de TypeScript pour être productif! »

    De TypeScript 3 à TypeScript 4

    TypeScript fait aujourd'hui partie intégrante de la pile JavaScript de nombreuses personnes. Sur npm, TypeScript a enregistré plus de 50 millions de téléchargements mensuels pour la première fois en juillet!

    À partir de la version 3.0, il y a un grand nombre de changements. L'unification des types de tuples et des listes de paramètres a été parmi les éléments mis en exergue. La version comprenait également des références de projet pour aider à augmenter, organiser et effectuer des partages entre les bases de code. Un petit changement qui a eu un grand impact a été que la 3.0 a introduit une alternative de type sûr à any appelé unknown.

    TypeScript 3.1 a étendu les capacités des types mappés pour fonctionner sur les types de tuple et de tableau, et a considérablement simplifié l'attachement de propriétés aux fonctions sans recourir à des fonctionnalités d'exécution spécifiques à TypeScript qui ne sont plus utilisées.

    TypeScript 3.2 permettait la diffusion d'objets sur des types génériques et exploitait les capacités de la 3.0 pour mieux modéliser la métaprogrammation avec des fonctions en tapant strictement bind, call et apply. TypeScript 3.3 s'est un peu concentré sur la stabilité après 3.2, mais a également apporté des améliorations lors de l'utilisation de méthodes de type union, et a ajouté des builds incrémentiels de fichier en mode --build.

    Dans la version 3.4, Microsoft s'est penché davantage sur la prise en charge des modèles fonctionnels, avec une meilleure prise en charge des structures de données immuables et une meilleure inférence sur les fonctions génériques d'ordre supérieur. Comme bonus, cette version a introduit l'indicateur --incremental, un moyen d'obtenir des compilations et des vérifications de type plus rapides en évitant une reconstruction complète à chaque exécution de TypeScript, sans références de projet.

    Avec TypeScript 3.5 et 3.6, nous avons assisté à un resserrement des règles du système de types, ainsi qu'à des règles de vérification de compatibilité plus intelligentes.

    TypeScript 3.7 était une version très remarquable, car elle présentait une riche combinaison de nouvelles fonctionnalités de système de type avec des fonctionnalités ECMAScript. Du côté du système de type, nous avons vu des références d'alias de type récursives et la prise en charge des fonctions de style assertion, deux caractéristiques uniques du système de type. Du côté de JavaScript, la version a apporté le chaînage et la fusion facultatifs, deux des fonctionnalités les plus demandées par les utilisateurs de TypeScript et de JavaScript.

    Beaucoup plus récemment, TypeScript 3.8 et 3.9 ont apporté des importations / exportations de type uniquement, ainsi que des fonctionnalités ECMAScript telles que les champs privés, await dans les modules et de nouvelles syntaxes export *. Ces versions ont également permis d'optimiser les performances et l'évolutivité.

    Passons en revue quelques nouvelles fonctionnalités de TypeScript 4.0.

    Nom : type.png
Affichages : 35628
Taille : 89,1 Ko

    Types de tuple variadique (qui accepte un nombre variable de paramètres)

    Considérez une fonction en JavaScript appelée concat, qui prend deux types de tableau ou de tuple et les concatène ensemble comme un nouveau tableau.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function concat(arr1, arr2) {
        return [...arr1, ...arr2];
    }

    Considérez également tail, qui prend un tableau ou un tuple, et renvoie tous les éléments sauf le premier.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function tail(arg) {
        const [_, ...result] = arg;
        return result
    }

    Comment pourrions-nous taper l'un de ces éléments dans TypeScript?

    Pour concat, la seule chose valable que nous pouvions faire dans les anciennes versions du langage était d'essayer d'écrire des surcharges. Une fonction surchargée est une fonction qui est définie plusieurs fois dans une application afin d’offrir différentes logiques à l’application.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function concat<>(arr1: [], arr2: []): [A];
    function concat<A>(arr1: [A], arr2: []): [A];
    function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
    function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
    function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
    function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
    function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];)

    Ce qui nous donne donc sept surcharges lorsque le deuxième tableau est toujours vide. Ajoutons-en pour quand arr2 a un argument.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function concat<A2>(arr1: [], arr2: [A2]): [A2];
    function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2];
    function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
    function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
    function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
    function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
    function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];

    Il est donc clair que cela devient déraisonnable. Malheureusement, vous vous retrouveriez également avec les mêmes types de problèmes lors de la saisie d'une fonction comme tail.

    C'est un autre cas de ce que l'équipe TypeScript aime appeler « la mort par mille surcharges », et cela ne résout même pas le problème en général. Il ne donne que les types corrects pour autant de surcharges que nous voulons écrire. Si nous voulions créer un cas fourre-tout, nous aurions besoin d'une surcharge comme celle-ci:

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    function concat<T, U>(arr1: T[], arr2, U[]): Array<T | U>;

    Mais cette signature n'encode rien à propos de la longueur de l'entrée ou l'ordre des éléments lors de l'utilisation de tuples.

    TypeScript 4.0 apporte deux changements fondamentaux, ainsi que des améliorations d'inférence, pour rendre leur saisie possible.

    Le premier changement est que les spreads (Spread ou REST paramètre est une technique qui peut être utilisée dans le cas où nous ne connaissons pas le nombre d'arguments qui sera envoyé à la fonction, un paramètre spread est dénoté par trois points (...) qui se placent devant le nom de paramètre) dans la syntaxe de type tuple peuvent désormais être génériques. Cela signifie que nous pouvons représenter des opérations d'ordre supérieur sur des tuples et des tableaux même lorsque nous ne connaissons pas les types réels sur lesquels nous opérons. Lorsque des spreads génériques sont instanciés (ou remplacés par un type réel) dans ces types de tuples, ils peuvent produire d'autres ensembles de types de tableau et de tuple.

    Par exemple, cela signifie que nous pouvons taper une fonction comme queue, sans notre problème de «mort par mille surcharges».

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function tail<T extends any[]>(arr: readonly [any, ...T]) {
        const [_ignored, ...rest] = arr;
        return rest;
    }
     
    const myTuple = [1, 2, 3, 4] as const;
    const myArray = ["hello", "world"];
     
    // type [2, 3, 4]
    const r1 = tail(myTuple);
     
    // type [2, 3, ...string[]]
    const r2 = tail([...myTuple, ...myArray] as const);

    Le deuxième changement est que les éléments spread peuvent apparaître n'importe où dans un tuple - pas seulement à la fin!

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Strings = [string, string];
    type Numbers = [number, number];
     
    // [string, string, number, number]
    type StrStrNumNum = [...Strings, ...Numbers];

    Auparavant, TypeScript générait une erreur comme celle-ci.

    Citation Envoyé par TypeScript
    A rest element must be last in a tuple type.
    Mais maintenant, le langage peut aplanir les spreads à n'importe quelle position.

    Lorsque nous répartissons un type sans longueur connue, le type résultant devient également illimité et tous les éléments consécutifs sont pris en compte dans le type d'élément de repos résultant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Strings = [string, string];
    type Numbers = number[]
     
    // [string, string, ...Array<number | boolean>]
    type Unbounded = [...Strings, ...Numbers, boolean];
    En combinant ces deux comportements ensemble, nous pouvons écrire une seule signature bien typée pour concat:

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Arr = readonly any[];
     
    function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
        return [...arr1, ...arr2];
    }

    Bien que cette signature soit encore un peu longue, il ne s'agit toujours que d'une signature, elle ne doit être écrite qu'une seule fois, et elle donne en fait un comportement prévisible sur tous les tableaux et tuples.

    Si l'équipe est plutôt satisfaite de cette fonctionnalité, elle reconnaît qu'il existe également d'autres scénarios plus sophistiqués. Par exemple, considérons une fonction pour appliquer partiellement des arguments appelés partialCall.partialCall qui prend une fonction avec les quelques arguments initiaux attendus par cette fonction. Elle renvoie ensuite une nouvelle fonction qui prend tous les autres arguments dont la fonction a besoin et les appelle ensemble.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function partialCall(f, ...headArgs) {
        return (...tailArgs) => f(...headArgs, ...tailArgs)
    }

    TypeScript 4.0 améliore le processus d'inférence pour les paramètres de repos et les éléments de tuple de repos.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type Arr = readonly unknown[];
     
    function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) {
        return (...b: U) => f(...headArgs, ...b)
    }

    Dans ce cas, partialCall comprend les paramètres qu'il peut et ne peut pas prendre initialement et renvoie des fonctions qui acceptent et rejettent de manière appropriée tout ce qui reste.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const foo = (x: string, y: number, z: boolean) => {}
     
    // This doesn't work because we're feeding in the wrong type for 'x'.
    const f1 = partialCall(foo, 100);
    //                          ~~~
    // error! Argument of type 'number' is not assignable to parameter of type 'string'.
     
     
    // This doesn't work because we're passing in too many arguments.
    const f2 = partialCall(foo, "hello", 100, true, "oops")
    //                                              ~~~~~~
    // error! Expected 4 arguments, but got 5.
     
     
    // This works! It has the type '(y: number, z: boolean) => void'
    const f3 = partialCall(foo, "hello");
     
    // What can we do with f3 now?
     
    f3(123, true); // works!
     
    f3();
    // error! Expected 2 arguments, but got 0.
     
    f3(123, "hello");
    //      ~~~~~~~
    // error! Argument of type '"hello"' is not assignable to parameter of type 'boolean'.

    Source : billet TypeScript
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

  3. #3
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Mars 2013
    Messages
    8 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Mars 2013
    Messages : 8 461
    Points : 197 892
    Points
    197 892
    Par défaut TypeScript 4.1 apporte le remappage de clé dans les types mappés, les types template de littéraux
    TypeScript 4.1 apporte le remappage de clé dans les types mappés, les types template de littéraux
    les types conditionnels récursifs et bien d'autres

    Microsoft a annoncé la disponibilité de TypeScript 4.1, la première mise à niveau de TypeScript 4.0. Au sujet de cette dernière (TypeScript 4.0), Microsoft avait déclaré :

    « Cette version du langage représente notre prochaine génération de versions de TypeScript, alors que nous approfondissons l'expressivité, la productivité et l'évolutivité.

    « Si vous n'êtes pas familier avec TypeScript, c'est un langage qui s'appuie sur JavaScript en ajoutant une syntaxe pour les types statiques. L'idée est qu'en écrivant les types de vos valeurs et où elles sont utilisées, vous pouvez utiliser TypeScript pour vérifier le type de votre code et vous informer des erreurs avant d'exécuter votre code (et même avant d'enregistrer votre fichier). Vous pouvez ensuite utiliser le compilateur TypeScript pour supprimer les types de votre code et vous laisser avec un JavaScript propre et lisible qui s'exécute n'importe où. Au-delà de la vérification, TypeScript utilise également des types statiques pour alimenter d'excellents outils d'édition tels que l'autocomplétion, la navigation dans le code, les refactorisations, etc. En fait, si vous avez utilisé JavaScript dans un éditeur tel que Visual Studio Code ou Visual Studio, vous avez déjà utilisé une expérience optimisée par types et TypeScript.

    « Avec TypeScript 4.0, il n'y a pas de changements majeurs de rupture. En fait, si vous êtes nouveau dans le langage, c'est le meilleur moment pour commencer à l'utiliser. La communauté est déjà là et grandit, avec un code fonctionnel et de nouvelles ressources à apprendre. Et une chose à garder à l'esprit: malgré toutes les bonnes choses que nous apportons dans la version 4.0, il vous suffit de connaître les bases de TypeScript pour être productif! »

    Types template de littéraux

    Les types littéraux de chaîne de caractères dans TypeScript nous permettent de modéliser des fonctions et des API qui attendent un ensemble de chaînes spécifiques de caractères.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function setVerticalAlignment(pos: "top" | "middle" | "bottom") {
        // ...
    }
     
    setVerticalAlignment("middel");
    //                   ~~~~~~~~
    // error: Argument of type '"middel"' is not assignable to
    //        parameter of type '"top" | "middle" | "bottom"'.

    C'est plutôt bien, car les types littéraux de chaîne de caractères peuvent essentiellement vérifier l'orthographe de nos valeurs de chaîne.

    Certains développeurs aiment également que les littéraux de chaîne de caractères puissent être utilisés comme noms de propriété dans les types mappés. En ce sens, ils peuvent également être utilisés comme éléments de base.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    type Options = {
        [K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean
    };
    // same as
    //   type Options = {
    //       noImplicitAny?: boolean,
    //       strictNullChecks?: boolean,
    //       strictFunctionTypes?: boolean
    //   };

    Mais il existe un autre cas où ces types littéraux de chaîne de caractères peuvent être utilisés comme blocs de construction : la construction d'autres types littéraux de chaîne de caractères.

    C’est pourquoi TypeScript 4.1 apporte le type template de littéraux de chaîne. Il a la même syntaxe que les templates de littéraux de chaînes de caractères en JavaScript, mais est utilisé dans les positions de type. Lorsque vous l'utilisez avec des types littéraux concrets, il produit un nouveau type littéral de chaîne en concaténant le contenu.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type World = "world";
     
    type Greeting = `hello ${World}`;
    // same as
    //   type Greeting = "hello world";

    Que se passe-t-il lorsque des unions occupent des positions de substitution ? Il produit l'ensemble de tous les littéraux de chaîne possibles qui pourraient être représentés par chaque membre d'union.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type Color = "red" | "blue";
    type Quantity = "one" | "two";
     
    type SeussFish = `${Quantity | Color} fish`;
    // same as
    //   type SeussFish = "one fish" | "two fish"
    //                  | "red fish" | "blue fish";

    Cela peut être utilisé au-delà des exemples dans les notes de publication. Par exemple, plusieurs bibliothèques de composants d'interface utilisateur ont un moyen de spécifier un alignement vertical et horizontal dans leurs API, souvent les deux à la fois en utilisant une seule chaîne comme "bottom-right" (en bas à droite). Entre l'alignement vertical avec "top" (haut), "middle" (milieu) et "bottom" (bas) et l'alignement horizontal avec "left", "center" et "right", il y a 9 chaînes possibles où chacune des anciennes chaînes est connectée à chacune des les dernières chaînes en utilisant un tiret.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    type VerticalAlignment = "top" | "middle" | "bottom";
    type HorizontalAlignment = "left" | "center" | "right";
     
    // Takes
    //   | "top-left"    | "top-center"    | "top-right"
    //   | "middle-left" | "middle-center" | "middle-right"
    //   | "bottom-left" | "bottom-center" | "bottom-right"
    declare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;
     
    setAlignment("top-left");   // works!
    setAlignment("top-middel"); // error!
    setAlignment("top-pot");    // error! but good doughnuts if you're ever in Seattle

    Bien qu'il existe de nombreux exemples de ce type d'API, il s'agit toujours d'exemple assez simple dans la mesure où nous pourrions les écrire manuellement. En fait, pour 9 chaînes, c'est probablement très bien; mais lorsque vous avez besoin d'une tonne de chaînes, vous devriez envisager de les générer automatiquement à l'avance pour économiser du travail sur chaque vérification de type (ou simplement utiliser un string, ce qui sera beaucoup plus simple à comprendre).

    Une partie de la valeur réelle provient de la création dynamique de nouveaux littéraux de chaîne. Par exemple, imaginez une API makeWatchedObject qui prend un objet et produit un objet pratiquement identique, mais avec une nouvelle méthode on pour détecter les modifications des propriétés.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    let person = makeWatchedObject({
        firstName: "Homer",
        age: 42, // give-or-take
        location: "Springfield",
    });
     
    person.on("firstNameChanged", () => {
        console.log(`firstName was changed!`);
    });

    Notez que on écoute l'événement "firstNameChanged", pas seulement "firstName". Comment écririons-nous cela?

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type PropEventSource<T> = {
        on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
    };
     
    /// Create a "watched object" with an 'on' method
    /// so that you can watch for changes to properties.
    declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;

    Avec cela, nous pouvons créer quelque chose qui se trompe lorsque nous attribuons la mauvaise propriété!

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    // error!
    person.on("firstName", () => {
    });
     
    // error!
    person.on("frstNameChanged", () => {
    });

    Nom : type.png
Affichages : 81008
Taille : 89,1 Ko

    Nous pouvons également faire quelque chose de spécial dans les types template de littéraux : nous pouvons déduire des positions de substitution. Nous pouvons rendre notre dernier exemple générique pour déduire des parties de la chaîne eventName pour déterminer la propriété associée.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    type PropEventSource<T> = {
        on<K extends string & keyof T>
            (eventName: `${K}Changed`, callback: (newValue: T[K]) => void ): void;
    };
     
    declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
     
    let person = makeWatchedObject({
        firstName: "Homer",
        age: 42,
        location: "Springfield",
    });
     
    // works! 'newName' is typed as 'string'
    person.on("firstNameChanged", newName => {
        // 'newName' has the type of 'firstName'
        console.log(`new name is ${newName.toUpperCase()}`);
    });
     
    // works! 'newAge' is typed as 'number'
    person.on("ageChanged", newAge => {
        if (newAge < 0) {
            console.log("warning! negative age");
        }
    })

    Ici, nous avons transformé on en méthode générique. Lorsqu'un utilisateur fait un appel avec la chaîne «firstNameChanged», TypeScript essaiera de déduire le bon type pour K. Pour ce faire, il comparera K au contenu antérieur à «Changed» et en déduira la chaîne «firstName». Une fois que TypeScript l'a déterminé, la méthode on peut récupérer le type de firstName sur l'objet d'origine, qui est string dans ce cas. De même, lorsque nous faisons un appel avec "ageChanged", elle trouve le type de la propriété age qui est number.

    L'inférence peut être combinée de différentes manières, souvent pour déconstruire des chaînes et les reconstruire de différentes manières. En fait, pour vous aider à modifier ces types littéraux de chaîne, l'équipe a ajouté quelques nouveaux alias de type d'utilitaire pour modifier la casse des lettres (c'est-à-dire la conversion en caractères minuscules et majuscules).

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type EnthusiasticGreeting<T extends string> = `${Uppercase<T>}`
     
    type HELLO = EnthusiasticGreeting<"hello">;
    // same as
    //   type HELLO = "HELLO";

    Les nouveaux alias de type sont Uppercase (majuscule), Lowercase (minuscule), Capitalize et Uncapitalize. Les deux premiers transforment chaque caractère d'une chaîne et les deux derniers ne transforment que le premier caractère d'une chaîne.

    Remappage de clé dans les types mappés

    Pour rappel, un type mappé peut créer de nouveaux types d'objets basés sur des clés arbitraires :

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    type Options = {
        [K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean
    };
    // same as
    //   type Options = {
    //       noImplicitAny?: boolean,
    //       strictNullChecks?: boolean,
    //       strictFunctionTypes?: boolean
    //   };

    ou de nouveaux types d'objets basés sur d'autres types d'objets :

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    /// 'Partial<T>' is the same as 'T', but with each property marked optional.
    type Partial<T> = {
        [K in keyof T]?: T[K]
    };

    Jusqu'à présent, les types mappés ne pouvaient produire que de nouveaux types d'objets avec les clés que vous leur fournissiez; cependant, vous voulez souvent pouvoir créer de nouvelles clés ou filtrer les clés en fonction des entrées.

    C’est pourquoi TypeScript 4.1 vous permet de remapper les clés dans les types mappés avec une nouvelle clause as.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type MappedTypeWithNewKeys<T> = {
        [K in keyof T as NewKeyType]: T[K]
        //            ^^^^^^^^^^^^^
        //            This is the new syntax!
    }

    Avec cette nouvelle clause as, vous pouvez tirer parti de fonctionnalités telles que les types template de littéraux pour créer facilement des noms de propriété basés sur d'anciens.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type Getters<T> = {
        [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
    };
     
    interface Person {
        name: string;
        age: number;
        location: string;
    }
     
    type LazyPerson = Getters<Person>;

    et vous pouvez même filtrer les clés en produisant never. Cela signifie que vous n’avez pas besoin d’utiliser un type d’assistant Omit supplémentaire dans certains cas.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Remove the 'kind' property
    type RemoveKindField<T> = {
        [K in keyof T as Exclude<K, "kind">]: T[K]
    };
     
    interface Circle {
        kind: "circle";
        radius: number;
    }
     
    type KindlessCircle = RemoveKindField<Circle>;
    // same as
    //   type KindlessCircle = {
    //       radius: number;
    //   };

    Types conditionnels récursifs

    En JavaScript, il est assez courant de voir des fonctions qui peuvent aplatir et créer des types de conteneurs à des niveaux arbitraires. Par exemple, considérons la méthode .then() sur les instances de Promise. .then(...) décompresse chaque Promise jusqu'à ce qu'il trouve une valeur qui ne ressemble pas à une Promise et transmet cette valeur à une fonction de rappel (callback). Il existe également une méthode flat relativement nouvelle sur les Array qui peut prendre une profondeur d'aplatissement.

    Exprimer cela dans le système de types de TypeScript n’était, à toutes fins pratiques, pas possible. Bien qu'il y ait eu des hacks pour y parvenir, les types ont fini par sembler très déraisonnables.

    C’est pourquoi TypeScript 4.1 assouplit certaines restrictions sur les types conditionnels, afin qu’ils puissent modéliser ces modèles. Dans TypeScript 4.1, les types conditionnels peuvent désormais se référencer immédiatement dans leurs branches, ce qui facilite l'écriture d'alias de type récursif.

    Par exemple, si nous voulions écrire un type pour obtenir les types d'éléments des tableaux imbriqués, nous pourrions écrire le type deepFlatten suivant.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type ElementType<T> =
        T extends ReadonlyArray<infer U> ? ElementType<U> : T;
     
    function deepFlatten<T extends readonly unknown[]>(x: T): ElementType<T>[] {
        throw "not implemented";
    }
     
    // All of these return the type 'number[]':
    deepFlatten([1, 2, 3]);
    deepFlatten([[1], [2, 3]]);
    deepFlatten([[1], [[2]], [[[3]]]]);

    De même, dans TypeScript 4.1, nous pouvons écrire un type Awaited pour développer profondément les Promise.

    Code TypeScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
     
    /// Like `promise.then(...)`, but more accurate in types.
    declare function customThen<T, U>(
        p: Promise<T>,
        onFulfilled: (value: Awaited<T>) => U
    ): Promise<Awaited<U>>;

    Gardez à l'esprit que si ces types récursifs sont puissants, ils doivent être utilisés de manière responsable et avec parcimonie.

    Tout d'abord, ces types peuvent faire beaucoup de travail, ce qui signifie qu'ils peuvent augmenter le temps de vérification de type. Essayer de numéros de modèle dans la conjecture de Collatz ou la séquence de Fibonacci peut être amusant, mais ne les livrez pas dans des fichiers .d.ts sur npm.

    En plus d'être gourmand en puissance de calcul, ces types peuvent atteindre une limite de profondeur de récursivité interne sur des entrées suffisamment complexes. Lorsque cette limite de récursivité est atteinte, cela entraîne une erreur de compilation. En général, il vaut mieux ne pas utiliser du tout ces types que d’écrire quelque chose qui échoue sur des exemples plus réalistes.

    Source : Microsoft
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

Discussions similaires

  1. Chrome 21 beta permet d’utiliser la Webcam sans plugin
    Par Hinault Romaric dans le forum Google Chrome
    Réponses: 11
    Dernier message: 09/08/2012, 14h23
  2. Réponses: 0
    Dernier message: 08/09/2010, 00h23
  3. Réponses: 13
    Dernier message: 11/06/2010, 11h04
  4. Réponses: 0
    Dernier message: 26/03/2010, 08h07
  5. Réponses: 0
    Dernier message: 01/10/2009, 00h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo