Maison  >  Questions et réponses  >  le corps du texte

Comment attribuer un type à un tableau de tuples dont les entrées peuvent varier d'un tuple à l'autre ?

En supposant que je l'ai fait function foo(args) {...},其中args是一个二元组数组,这样元组中的条目是相同类型的(即[T,T]),但是元组的条目可能任意变化(即[[T,T], [U,U],[V,V]]). Par exemple :

foo([
  [1, 3],
  ["hello", "world"],
  [true, true],
  [2, 7]
]) // no error

Comment dois-je saisir le paramètre fooargs pour que les types incompatibles dans le tuple génèrent une erreur de type au moment de la compilation ? Par exemple :

foo([
  [1, 3],
  ["hello", 5], // type error here
  [true, true],
  [2, 7n] // type error here
])

Si l'erreur de type ne peut pas être affichée en ligne, il est également acceptable que l'intégralité de l'appel de fonction soit erronée.


Annexe : Est-elle disponible [SomeType, T] 类型的 2 元组(即第二个条目的类型应与第一个),但 T 仍然可以在元组之间变化 [[SomeType, T],[SomeType, U],[SomeType, V]] ?

foo([
  [{value: 1}, 3],
  [{value: "hello"}, 5], // type error here
  [{value: true}, true],
  [{value: 2}, 7n] // type error here
])

P粉068486220P粉068486220409 Il y a quelques jours527

répondre à tous(2)je répondrai

  • P粉948258958

    P粉9482589582023-09-07 19:10:47

    Je pense que tu peux passer pour row 创建一个类型来简单地实现此目的,该类型将接受 stringnumber 或 <代码>布尔值.

    类型 Row = string[] |布尔值[] |数字[]

    Nous pouvons maintenant attribuer ce type au paramètre foo 函数的 args.

    function foo(args: Row[]): void {
     ...
     ...
     ...
    }

    En utilisant cette définition de type, Typescript générera une erreur si vous fournissez un argument à foo où les types d'éléments de la ligne ne correspondent pas.

    C'est le terrain de jeuLien.

    répondre
    0
  • P粉136356287

    P粉1363562872023-09-07 13:38:15

    Pour y parvenir, nous devons utiliser des genericsarrays et des mapped types pour mapper les éléments du tableau. Puisque nous savons que le tableau doit être un tableau de tuples de longueur 2, nous allons déduire le paramètre générique pour le premier élément du tuple et faire en sorte que le deuxième élément ait le même type. Pour obtenir le type d'un paramètre générique, nous devons utiliser le mot-clé Infer. Notez que nous devons connaître exactement (ou au moins un type de forme similaire) le type générique utilisé pour le faire fonctionner, qui dans notre cas est Variable :

    const foo = <T extends unknown[][]>(arr: {
      [K in keyof T]: T[K] extends unknown[]
        ? T[K][0] extends Variable<infer Type>
          ? [Variable<Type>, Type]
          : T[K]
        : T[K];
      }) => {}

    Cela peut sembler être tout, mais regardons les types des tableaux suivants :

    const arr = [1, '2', false];
    // (string | number | boolean)[]
    type Arr = typeof arr;

    Comme vous pouvez le constater, le type n'est pas exactement le même que celui que nous avons dans l'arr. Le compilateur étend le type pour garantir que nous pouvons modifier les éléments du tableau. Pour faire savoir au compilateur que le tableau est en lecture seule, nous devons utiliser l'assertion const :

    const arr = [1, '2', false] as const;
    // readonly [1, "2", false]
    type Arr = typeof arr;

    Ça a l'air bien maintenant, cela signifie que nous devons définir le tableau transmis à foo en lecture seule, et comme les tableaux en lecture seule sont un sur-ensemble des tableaux mutables que nous obtiendrions si nous essayions de transmettre un tableau en lecture seule à An une erreur apparaîtra :

    // false
    type Case1 = readonly number[] extends number[] ? true : false;
    // true
    type Case2 = number[] extends readonly number[] ? true : false;

    Nous mettons donc à jour tous les types de tableaux dans foo pour qu'ils soient en lecture seule. Notez que puisque notre tableau est 2D, le tableau interne sera également en lecture seule, et les contraintes sur le tableau doivent être un tableau en lecture seule de tableaux en lecture seule :

    const foo = <T extends readonly (readonly unknown[])[]>(arr: {
      [K in keyof T]: T[K] extends readonly unknown[]
        ? T[K][0] extends Variable<infer Type>
          ? readonly [Variable<Type>, Type]
          : T[K]
        : T[K];
    }) => {};

    Test :

    declare const ctx1: Variable<number>;
    declare const ctx2: Variable<string>;
    declare const ctx3: Variable<boolean>;
    declare const ctx4: Variable<number>;
    declare const ctx5: Variable<number[]>;
    declare const ctx6: Variable<{ name: string; age: number }>;
    
    foo([
      [ctx1, 3],
      [ctx2, 'world'],
      [ctx3, true],
      [ctx4, 7],
    ] as const);
    
    foo([
      [ctx1, 3],
      [ctx2, 'world'],
      [ctx3, true],
      [ctx4, 'invalid'], // error
    ] as const);

    Cependant, nous avons encore quelques problèmes. Par exemple, si le premier élément du tuple est une primitive de Variable<7> ,则意味着第二个参数也应该是 7,而不是任何数字,如果这是一个问题我们需要获取 7, qui est un nombre. Cela peut être fait en utilisant les types d'utilitaires ToPrimitive de mon projet open source type-samurai :

    type ToPrimitive<T> = T extends string
      ? string
      : T extends number
      ? number
      : T extends null
      ? null
      : T extends undefined
      ? undefined
      : T extends boolean
      ? boolean
      : T extends bigint
      ? bigint
      : T extends symbol
      ? symbol
      : {
          [K in keyof T]: ToPrimitive<T[K]>;
        };

    Fonctionnalités mises à jour :

    const foo = <T extends readonly (readonly unknown[])[]>(arr: {
      [K in keyof T]: T[K] extends readonly unknown[]
        ? T[K][0] extends Variable<infer Type>
          ? ToPrimitive<Type> extends infer PrimitiveType
            ? readonly [Variable<PrimitiveType>, PrimitiveType]
            : T[K]
          : T[K]
        : T[K];
    }) => {};

    Un autre problème est que si le type déduit dans notre implémentation actuelle foo 实现中推断的类型是 number[] est number[]

    , nous n'autoriserons pas les tableaux en lecture seule :

    foo([
      [ctx5, [4, 5, 6]], // The type 'readonly [4, 5, 6]' is 'readonly' and cannot be assigned to the mutable type 'number[]'
    ] as const)
    Le correctif est très simple, nous allons vérifier si le type déduit est un tableau, puis obtenir son type d'élément et ElementType en lecture seule[]

    comme deuxième paramètre du tuple :

    const foo = <T extends readonly (readonly unknown[])[]>(arr: {
      [K in keyof T]: T[K] extends readonly unknown[]
        ? T[K][0] extends Variable<infer Type>
          ? ToPrimitive<Type> extends infer PrimitiveType
            ? readonly [
                Variable<PrimitiveType>,
                PrimitiveType extends Array<infer ArrayItem>
                  ? readonly ArrayItem[]
                  : PrimitiveType,
              ]
            : T[K]
          : T[K]
        : T[K];
    }) => {};

    Test :🎜
    foo([
      [ctx1, 3],
      [ctx2, 'world'],
      [ctx3, true],
      [ctx4, 7],
      [ctx5, [4, 5, 6]],
      [ctx6, {name: "Hi", age: 23}],
    ] as const);
    
    foo([
      [ctx1, 3],
      [ctx2, 'world'],
      [ctx3, true],
      [ctx4, true], // error here
      [ctx5, [4, 5, 6]],
      [ctx6, 50], // error here
    ] as const);

    Ce qui est ennuyeux, c'est qu'il faut utiliser const 断言。在 Typescript 5.0 中,const 类型参数,这样我们就可以避免 const 断言 partout :

    const foo = <const T extends readonly unknown[]>(item: T) => item
    // readonly [1, 2, 3] 
    const result = foo([1,2,3])

    Malheureusement, nous ne pouvons pas les utiliser car nous faisons quelque chose avec le paramètre au lieu de lui attribuer directement T comme type :

    const foo = <const T extends readonly unknown[]>(item: {[K in keyof T]: T[K]}) => item
    
    // const result: (2 | 1 | 3)[]
    const result = foo([1, 2, 3])

    Quoi qu'il en soit, actuellement, const 断言 est le seul moyen de garantir que cela fonctionne comme prévu.

    Lien vers l'aire de jeux

    répondre
    0
  • Annulerrépondre