microsoft / microsoft/TypeScript

Allow object types to have property-like associated types

Abierto
#17,588 36 comentarios 74 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

In Discussion Suggestion
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
2 d 4 h
PR fusionados (30 d)
132

Descripción

Edit: Add static Type variant for classes, make abstractness a little clearer, clarify things.

This has probably already been asked before, but a quick search didn't turn up anything.

This is similar, and partially inspired by, Swift's/Rust's associated types and Scala's abstract types.


Rationale

  • In complex structures like in virtual DOM components/nodes and in graphs, where there are numerous constraints to check, but they would get cumbersome in a hurry to force off onto the user.
  • In dynamic imports, where you never have the module namespace itself until runtime, yet you still want to be able to use types defined within it. Edit: Already addressed elsewhere.
  • In types where there's a lot of optional parameters, it'd be easier and more flexible to label which ones you're defining as part of the type.
  • In types where you want to specify a single optional parameter, and the required ordering requires you to specify some that you don't want to specify, that would get boilerplatey in a hurry. I've experienced this personally on multiple occasions.
  • It would allow fully typing namespaces and named module imports without hard-coded compiler handling.

Proposed Syntax/Semantics

  • Each interface and object type may have a number of associated types within it.
  • Associated types are checked for assignability like any other usual property, except they live in a different "namespace" from properties. In particular, the type must be assignable to the target's type.
  • To access an associated type, you use TypeName.Type, where Type is the name of an associated type.
    • For sugar and namespace compatibility, object.Type is equivalent to (typeof object).Type
  • To expect an interface with an associated type, you use Foo & {type Type: Value} or Foo with <Type: Value>.
  • To expect an object with an associated type, you use {type Type: Value}.
  • To declare an abstract associated type, you use type Type: * within the interface or object type.
  • To declare a non-abstract associated type, you use type Type: Default within the interface or object type.
  • To constrain an abstract associated type, you use type Type: * extends Super.
  • Associated types may be inherited from other interfaces and/or object types.
  • Namespaces' types are modified to include the exported types as associated types.
  • Classes may also define associated types, optionally with visibility modifiers.
  • Associated types may have defaults, in case they aren't defined or further constrained later.
    • Constraining an associated type with an existing default removes the default if and only if the existing constraint is not assignable to the new constraint. For example, {type Foo: string | number = string} & {type Foo: string | number} is assignable to {type Foo: string}, but {type Foo: string | number = string} & {type Foo: number} is not.

Here's what that would look like in syntax:

// Interfaces
interface Foo {
    type Type: *; // abstract
    type Sub: * extends Type; // abstract, constrained
    type Type: Default;
    type Type: * extends Type = Default; // late-bound default
}

// Objects
type Foo = {
    type Type: Foo,
}

// Classes
abstract class Foo {
    // Note: outer class *must* be abstract for these, and the keyword is required.
    abstract type Type: *; // abstract
    private abstract type Sub: * extends Type; // abstract, constrained
    protected abstract type Type: * extends Type = Default, // late-bound default

    // Note: outer class *may* be not abstract for these.
    type Type: Default;
    private type Type: Default;

    // Declare an associated type in the class
    // Note: type must not be abstract.
    static Type: Foo;
}

Emit

This has no effect on the JavaScript emit as it is purely type-level.

Compatibility

  • This is purely additive, making no observably incompatible changes beyond possibly different error messages.
  • This has no impact on any existing JavaScript-related proposal.

Other

  • It may slow down the type checker a little initially when namespaces are unified, but two optimization points are available, which would recover most of the perf hit, if not all:
    • Associated types could be stored in a per-interface type map.
    • The list of associated types could be initially stored as undefined to avoid generating a large number of empty arrays.
  • It should have little effect on editor tooling.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

El issue no menciona archivos, pruebas ni puntos de entrada, así que comienza localizando el código del parser y del comprobador de tipos para interfaces, tipos de objeto, clases, namespaces y consultas de tipos. Lee las pruebas relevantes del compilador antes de proponer una implementación. Se considera terminado cuando la sintaxis y la semántica propuestas para tipos asociados estén implementadas, comprobadas en los casos indicados y se haya confirmado que no hay cambios en la emisión de JavaScript.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
compilers
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Activo
Claridad
Bastante claro
Aptitud para principiantes
30/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.