Saltar al contenido
webtype.orgwebtype.org

    ↑↓ mover · ⏎ abrir · esc cerrar

    Jugar

    Extends

    Un tipo a la izquierda, otro a la derecha, una tecla: ¿extiende? 586 cartas, y el compilador ya ha respondido a todas.

    Arcade · 586 cartas

    ¿Qué rama toma el compilador?

    Cada carta es A extends B ? true : false. Responde antes de que se consuma la mecha. Cinco seguidas suben el multiplicador; un fallo cuesta una de tus tres vidas y te enseña exactamente lo que dijo el compilador.

    Hoy, el mismo reparto para todos. La primera partida es la que cuenta.

    T←trueF→falseEscpausa⏎empezar

    Rango
    @ts-nocheck
    faltan 10 para @ts-check
    Racha diaria
    0
    Cartas dominadas
    0/586

    Puntuaciones, rangos y rachas se guardan en este navegador y en ningún otro sitio.

    01Cómo se juega

    Una pregunta, formulada de 586 maneras

    1. 01

      Lee el par

      Dos tipos y la palabra entre ellos. Algunas cartas traen antes una declaración que leer: una interfaz, un enum, una clase.

    2. 02

      Elige una rama

      true o false: T o ←, F o →, un toque, o desliza la carta hacia el lado que quieres.

    3. 03

      Gánale a la mecha

      Cada carta se consume. Un acierto vale 100 por nivel, más hasta la mitad por rapidez. Un fallo o quedarte sin tiempo cuesta una vida.

    4. 04

      Mantén la racha

      Cada cinco seguidas el multiplicador sube, hasta ×8. Quince seguidas te devuelven una vida perdida.

    02La baraja

    36 reglas de asignabilidad

    Cada carta pertenece a una regla, y cada regla tiene cartas a ambos lados: los pares que pasan y los que no. Los cuadrados son tuyos: una carta que acertaste la última vez, una que fallaste, una que aún no has visto.

    01

    Literales y primitivos

    Un tipo literal es un único valor de su primitivo, así que lo extiende — nunca al revés, y nunca entre primitivos distintos: "42" es un string, no un number. Un literal numérico es su valor, se escriba como se escriba: 0x10 es 16.

    • "a" extends string
      true
    • string extends "a"
      TS2322
    Tu registro: 0 de 30

    Lección: Tipos condicionales

    02

    Hacia una unión

    Una unión a la derecha es una elección: lo que extiende a uno de sus miembros extiende la unión entera. PropertyKey es string | number | symbol.

    • "a" extends "a" | "b"
      true
    • "c" extends "a" | "b"
      TS2322
    Tu registro: 0 de 22

    Referencia: Extract

    03

    Desde una unión

    Una unión a la izquierda tiene que encajar entera: cada miembro debe extender el lado derecho. boolean no es más que true | false.

    • "a" | "b" extends string
      true
    • "a" | "b" extends "a"
      TS2322
    04

    null y undefined

    Con strictNullChecks, null y undefined son tipos propios. Solo encajan donde se los nombra — o en unknown, any y, en el caso de undefined, también en void.

    • undefined extends unknown
      true
    • null extends string
      TS2322
    05

    La cima: unknown

    Todo tipo extiende unknown. unknown solo extiende lo que admite cualquier valor: unknown, any — o {} | null | undefined, que es el mismo conjunto escrito por partes.

    • string extends unknown
      true
    • unknown extends string
      TS2322
    Tu registro: 0 de 16

    Error: TS18046

    06

    El fondo: never

    never es el tipo sin valores, así que lo extiende todo — y a él no lo extiende nada salvo el propio never.

    • never extends string
      true
    • string extends never
      TS2322
    07

    any apaga la comprobación

    Donde aparece any, esa parte de la comparación pasa, en cualquier dirección. Pero no cambia la estructura que lo rodea: una propiedad que falta o una longitud de tupla equivocada siguen fallando.

    • string extends any
      true
    • Record<string, any> extends { a: number }
      TS2741
    Tu registro: 0 de 15

    tsconfig: noImplicitAny

    08

    {} no es un objeto vacío

    {} significa «cualquier cosa salvo null o undefined», así que los primitivos lo extienden; object los deja fuera. Aun así {} extiende object mientras string extiende {}: la asignabilidad no es transitiva.

    • { a: string } extends {}
      true
    • null extends {}
      TS2322
    Tu registro: 0 de 20
    09

    String no es string

    Los nombres con mayúscula son interfaces de los objetos envoltorio. Un primitivo extiende su envoltorio, y cualquier tipo de objeto hecho con los miembros del envoltorio tal como este los tipa — el length de un string es un number, incluso para "abc". Un envoltorio nunca extiende al primitivo, y Object se comporta como {}.

    • string extends String
      true
    • String extends string
      TS2322
    Tu registro: 0 de 17
    10

    Las propiedades de más no molestan

    Los tipos de objeto son abiertos. Un tipo con más propiedades extiende a uno con menos — Error no es más que una interfaz con un nombre y un mensaje. Lo que falla es que falte una propiedad obligatoria.

    • { a: string; b: number } extends { a: string }
      true
    • { a: string } extends { a: string; b: number }
      TS2741
    Tu registro: 0 de 22

    Error: TS2741Error: TS2739

    11

    Propiedad a propiedad

    Las propiedades se comparan una a una, en la misma dirección que el todo: un tipo de propiedad más estrecho encaja, uno más ancho no.

    • { a: "x" } extends { a: string }
      true
    • { a: string } extends { a: "x" }
      TS2322
    Tu registro: 0 de 16

    Error: TS2322

    12

    Opcional no es | undefined

    Una propiedad opcional puede faltar del todo; a: T | undefined tiene que estar. Así que la obligatoria encaja en la opcional, y la opcional no encaja en la obligatoria.

    • { a: string } extends { a?: string }
      true
    • { a?: string } extends { a: string }
      TS2322
    13

    La comprobación de tipos débiles

    Un tipo cuyas propiedades son todas opcionales es «débil». Todo lo que tenga propiedades pero no comparta ninguna se rechaza con TS2559, aunque estructuralmente encajaría.

    • { a: string; b: number } extends { a?: string }
      true
    • { b: number } extends { a?: string }
      TS2559
    14

    Las propiedades readonly no cuentan

    readonly en una propiedad limita lo que puedes hacer a través de ese tipo, y la asignabilidad lo ignora: { readonly a: string } y { a: string } se extienden mutuamente.

    • { a: string } extends { readonly a: string }
      true
    • { readonly a: number } extends { a: string }
      TS2322
    15

    Los arrays son covariantes

    Un array de elementos más estrechos extiende a uno de elementos más anchos. Al escribir es inseguro — mete un número en un string[] visto como unknown[] — y TypeScript lo permite de todos modos.

    • string[] extends unknown[]
      true
    • string[] extends "a"[]
      TS2322
    Tu registro: 0 de 16
    16

    Los arrays readonly siguen siéndolo

    Un array o una tupla readonly no tiene métodos que muten, así que no puede ocupar un hueco mutable (TS4104). Un array mutable encaja sin queja en un hueco readonly.

    • string[] extends readonly string[]
      true
    • readonly string[] extends string[]
      TS4104
    17

    Las tuplas tienen longitud

    Una tupla es un array cuya longitud forma parte de su tipo — [1, 2] tiene length: 2. Las tuplas extienden arrays de sus elementos; un array nunca extiende una tupla, porque podría tener cualquier longitud.

    • [string, number] extends (string | number)[]
      true
    • (string | number)[] extends [string, number]
      TS2322
    18

    Los parámetros van al revés

    Con strictFunctionTypes, una función extiende a otra solo si acepta todo lo que la otra puede recibir: los tipos de los parámetros se comparan al revés, y a un parámetro opcional se le puede pasar undefined. Aceptar más es la dirección segura.

    • (x: string) => void extends (x: "a") => void
      true
    • (x: "a") => void extends (x: string) => void
      TS2322
    19

    Menos parámetros no es problema

    JavaScript deja que una función ignore sus argumentos, así que la que recibe menos parámetros extiende a la que recibe más. Lo que falla es exigir un argumento de más; un parámetro rest cuenta como cualquier número.

    • () => void extends (x: string) => void
      true
    • (x: string) => void extends () => void
      TS2322
    20

    Los retornos van hacia delante

    Los tipos de retorno se comparan en la misma dirección que las funciones: una función que devuelve algo más estrecho extiende a una que devuelve algo más ancho.

    • () => "a" extends () => string
      true
    • () => string extends () => "a"
      TS2322
    Tu registro: 0 de 18

    Referencia: ReturnType

    21

    void significa «ignorado»

    Un tipo de función que devuelve void solo promete que nadie leerá el resultado, así que lo extiende una función que devuelva cualquier cosa. Pero void no es ni undefined ni null: solo lo aceptan void, unknown y any.

    • () => number extends () => void
      true
    • () => void extends () => number
      TS2322
    Tu registro: 0 de 13

    Error: TS2355

    22

    Los métodos son bivariantes

    La sintaxis de método se salta strictFunctionTypes: los parámetros de un método se comparan en ambas direcciones, y lo que decide es la sintaxis del destino. Una propiedad con tipo de función recibe la comprobación estricta. Por eso Set<"a"> extiende Set<string>.

    • { m(x: string): void } extends { m(x: "a"): void }
      true
    • { m(x: number): void } extends { m(x: string): void }
      TS2322
    23

    Function, genéricos y constructores

    Toda función extiende Function, pero Function no promete ninguna firma concreta. Una función genérica extiende cada instancia que su restricción permite, no al revés — y un constructor abstracto no puede ocupar un hueco que exige new.

    • () => void extends Function
      true
    • Function extends () => void
      TS2322
    24

    Sobrecargas

    Un tipo con varias firmas de llamada extiende una firma única si cualquiera de sus sobrecargas encaja en ella. Al revés, una sola firma tiene que encajar en cada sobrecarga del destino, una por una.

    • { (x: string): string; (x: number): number } extends (x: string) => string
      true
    • (x: string) => string extends { (x: string): string; (x: number): number }
      TS2322
    Tu registro: 0 de 5

    Error: TS2769

    25

    Firmas de índice

    Una firma de índice exige que cada propiedad encaje en ella. Un literal de tipo objeto la recibe de forma implícita; una interfaz no, y object tampoco — así que ninguno extiende Record<string, unknown>, y la misma forma como alias de tipo sí. La excepción es una firma de tipo any: acepta cualquier objeto.

    • { a: "x" } extends { [k: string]: string }
      true
    • { a: string; b: number } extends Record<string, string>
      TS2322
    26

    Tipos de plantilla literal

    Un tipo de plantilla literal es un patrón sobre strings. ${string} encaja con cualquier texto, incluido el vacío; ${number}, con todo string no vacío que JavaScript lee como número finito — "1e3" y "0x1F" sí, "Infinity" no —, y ${bigint}, solo con los enteros. Lowercase<string> y sus parientes encajan con texto que ya está en esa caja.

    • "id-42" extends `id-${number}`
      true
    • "id-x" extends `id-${number}`
      TS2322
    27

    Intersecciones

    Una intersección tiene las propiedades de todos sus miembros, así que extiende a cada uno. La que ningún valor puede habitar — string & number, dos literales kind distintos — se reduce a never, y never lo extiende todo.

    • { a: string } & { b: number } extends { a: string }
      true
    • { a: string } extends { a: string } & { b: number }
      TS2322
    28

    Uniones de objetos

    Un objeto extiende una unión de objetos cuando encaja en uno de sus miembros. Desde TypeScript 3.5, un objeto cuyo discriminante es a su vez una unión se divide en un objeto por valor, y se comprueba cada pieza — por eso { kind: "a" | "b" } extiende { kind: "a" } | { kind: "b" }.

    • { kind: "a"; a: 1 } extends { kind: "a"; a: number } | { kind: "b" }
      true
    • { kind: "c" } extends { kind: "a" } | { kind: "b" }
      TS2322
    29

    keyof

    keyof es la unión de las claves conocidas de un tipo: las numéricas siguen siendo números, las de un array incluyen el nombre de cada método, y las de una firma de índice string son string | number. keyof {} y keyof unknown son never; keyof never es toda clave posible.

    • keyof { a: 1; b: 2 } extends "a" | "b"
      true
    • "a" | "b" | "c" extends keyof { a: 1; b: 2 }
      TS2322
    30

    Acceso indexado

    T[K] es el tipo de la propiedad K de T, y una unión de claves da una unión de tipos. En un array, T[number] es el tipo del elemento; en una tupla, la unión de sus elementos, y T["length"] su longitud literal. Una propiedad opcional trae consigo su undefined.

    • { a: string }["a"] extends string
      true
    • { a: string }["a"] extends number
      TS2322
    32

    Los tipos calculados se resuelven antes

    ReturnType, Parameters, Awaited, Exclude, Extract y NonNullable son tipos condicionales. El compilador resuelve cada uno al tipo corriente que produce antes de comparar nada — así que ReturnType<() => void> es void, y NonNullable<unknown> es {}.

    • ReturnType<() => string> extends string
      true
    • ReturnType<() => string> extends number
      TS2322
    33

    Las clases son estructurales — hasta private

    Dos clases con la misma forma pública se extienden mutuamente, y un tipo de objeto normal puede extender una clase; los miembros estáticos viven en el constructor, así que no cuentan. Un miembro private, protected o #private vuelve nominal a la clase: solo su propia declaración y sus subclases tienen ese miembro.

    • class Dog { name = "" } class Cat { name = "" }Cat extends Dog
      true
    • class A { x = 1 } class B extends A { y = 2 }A extends B
      TS2741
    Tu registro: 0 de 12

    Patrón: Marcar un primitivo

    34

    Enums

    Un enum numérico sigue aceptando cualquier number, y todo literal que sea uno de sus valores — pero no un literal fuera de rango. Cada miembro extiende su propio valor, así que S.A extiende "a"; pero un enum de strings solo acepta sus propios miembros, y "a" no es S.A.

    • enum E { A, B }E.A extends number
      true
    • enum E { A, B }E extends E.A
      TS2322
    Tu registro: 0 de 15

    tsconfig: erasableSyntaxOnly

    35

    unique symbol

    Un unique symbol es un literal de symbol: extiende symbol y PropertyKey, symbol no lo extiende a él, y dos de ellos nunca se extienden entre sí. Los símbolos conocidos, como Symbol.iterator, también son únicos.

    • typeof Symbol.iterator extends symbol
      true
    • symbol extends typeof Symbol.iterator
      TS2322
    Tu registro: 0 de 6

    Referencia: typeof

    36

    Genéricos y varianza

    Un tipo genérico se compara a través de su argumento, en la dirección en que el tipo lo usa: las propiedades y los retornos de forma covariante — también las mutables — y los parámetros de tipos de función de forma contravariante. in y out fijan la dirección explícitamente, por encima de lo que mediría el compilador; in out la fija como invariante.

    • Promise<"a"> extends Promise<string>
      true
    • Promise<string> extends Promise<"a">
      TS2322

    03Verificado

    El compilador repartió cada carta

    Cada carta se compila al construir el sitio con TypeScript 6.0.3, en modo estricto, dos veces: como el tipo condicional que muestra, que debe resolverse exactamente al veredicto de la carta, y como asignación, que debe fallar cuando el veredicto es false y pasar cuando es true. La construcción se detiene si alguna carta no coincide. El mensaje que lees tras un fallo es una cita de esa compilación, nunca algo escrito a mano.

    Cartas
    586
    Extienden
    340
    No extienden — cada una con su diagnóstico
    246
    Códigos de diagnóstico
    6
    TS2322 · TS2559 · TS2739 · TS2740 · TS2741 · TS4104

    Puntuaciones, rangos y rachas se guardan en este navegador y en ningún otro sitio.