Typescript: Clases parciales

Creado en 29 ago. 2014  ·  224Comentarios  ·  Fuente: microsoft/TypeScript

Agregue soporte de clases parciales. Mixins no es lo mismo, porque es realización en tiempo de ejecución. Necesita realización de compilación, donde las clases parciales se combinarán en una antes de convertir el mecanografiado a javascript.

//FileA.ts
partial class ClassA
{      
    constructor(public name: string) {}
    public sayHello(): string { return "hi!"; }
}

//FileB.ts
partial class ClassA
{
   public sayBye(): string { return "by!"; }
}

estarán:

partial class ClassA
{      
    constructor(public name: string) {}
    public sayHello(): string { return "hi!"; }
    public sayBye(): string { return "by!"; }
}
Out of Scope Suggestion

Comentario más útil

Un caso de uso para las clases parciales es para las clases de proxy generadas. Por ejemplo, envoltorios de WebAPI o SignalR. Sería realmente bueno poder extender las clases de proxy generadas con lógica personalizada. Especialmente al generar clases para modelos, sería bueno poder adjuntar la lógica de negocios directamente a las clases de modelo devueltas por la API.

Todos 224 comentarios

Creo que también emplea "parcial" en los módulos y ayuda a resolver este problema planteado aquí.

https://github.com/Microsoft/TypeScript/issues/447

@disshishkov quizás los métodos de extensión sean suficientes: # 9

¿Para qué tipo de situaciones desea utilizar esto y cómo las soluciones existentes no lo alcanzan?

Tenga en cuenta que esto se implementó en C # para admitir escenarios de edición de tipo WinForms donde tiene un archivo generado automáticamente y un archivo editado por el usuario que contribuye al mismo tipo; No estoy seguro de que ese tipo de situaciones se apliquen en JavaScript / TypeScript.

Algunas clases pueden tener muchos números de línea, solo para una mejor lectura, será útil dividir en varias clases separadas. Puede dividir por tipo diferente, por ejemplo, por lógica (en caso de que esta lógica no se pueda mover a otra clase), por visibilidad (privada y pública) y otros. En caso de que la combinación de clases parciales pueda tener problemas (por ejemplo, 2 clases parciales iguales, el mismo método / declaración de variable), el compilador debería notificar y arrojar un error.

Esto no es realmente convincente. Si su clase es tan grande que no se puede editar cómodamente en un solo archivo debido a su gran tamaño, es un olor de diseño muy importante (por ejemplo, comentarios en http://programmers.stackexchange.com/questions/157482). Dividir por diferente tipo / lógica / visibilidad / etc. es algo que podría ser manejado por un IDE o por una organización dentro de un solo archivo.

Vale la pena discutir por qué sus clases son tan grandes que no se puede navegar (¿es porque las herramientas de navegación deberían ser mejores?), Y si hay mejores formas en que el lenguaje podría ayudar a _descomponer_ una clase en lugar de simplemente _ dividirla.

Personalmente, creo que deberían existir "clases parciales", pero se comportan como módulos que se fusionan. Tengo un sistema con módulos que, aunque intellisense ve todos los módulos (como debería), el JS real que lo contiene solo se carga cuando es necesario. Creo que eso también sería genial para las clases: cargar solo las partes necesarias. También quería crear una función y usar una clase para expandirla más. Actualmente, solo puede hacer esto con módulos.

Un caso de uso para las clases parciales es para las clases de proxy generadas. Por ejemplo, envoltorios de WebAPI o SignalR. Sería realmente bueno poder extender las clases de proxy generadas con lógica personalizada. Especialmente al generar clases para modelos, sería bueno poder adjuntar la lógica de negocios directamente a las clases de modelo devueltas por la API.

+1 kvantetore.

El caso de uso es exactamente el mismo que .net; se genera una parte de la clase (en nuestro caso, a partir de esquemas Avro) y desea agregar código auxiliar adicional para trabajar con las clases generadas.

Me gusta esta sugerencia. Me gustaría tener clases parciales para separar los atributos / propiedades de las clases que forman mi "jerarquía de datos" que podrían reunirse en un solo archivo, de sus métodos que se pueden dividir en varios otros archivos. Haría el código más claro y más fácil de entender de un vistazo, en mi opinión.

Mi archivo de jerarquía de datos :

class A {
  x: number;
  y: number;
  z: number;
}

class B extends A {
  value: string;
  flag1: boolean;
  flag2: boolean;
}

Archivo que contiene métodos de clase A :

class A {
  constructor(x: number, y: number, z: number) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
  method1(...) { ... }
  ...
  methodN(...) { ... }
}

Archivo que contiene métodos de clase B :

class B extends A {
  constructor(x: number, y: number, z: number, value: string) {
    super(x, y, z);
    this.value = value;
    this.flag1 = false;
    this.flag2 = false;
  }
  method1(...) { ... }
  ...
  methodN(...) { ... }
}

+1 de mi parte. Me gustaría usar plantillas tt para generar clases de TypeScript, pero agregarlas en un archivo separado que no será reemplazado por la generación de plantillas.

+1 Esto realmente me ayudará con mis clases generadas automáticamente que necesito extender

La clase parcial es realmente agradable, pero no tiene sentido tenerla. Demostró que tienes un problema de diseño. La regla general en programación es mantenerlo simple y estúpido (KISS), independientemente de los idiomas que use, es dividir la clase grande en otras más pequeñas, ya sea 1) dividiendo los scripts en clases y llamarlos (otras clases también pueden llamar a eso clases más pequeñas también en lugar de reinventar la rueda), o 2) separarse y convertirse en abstracto / interfaz / herencia / virtual, etc. o polimorfismo.

Digamos que tiene un vehículo con todo lo que contiene. Una clase parcial no tiene sentido e introduce complejidad y gastos generales aquí donde tiene más sentido hacer esto en su lugar. Cree una clase de motor, una clase de Tranny, una clase de tren motriz, una clase de puerta y una clase de neumático como clases separadas y mueva los scripts a ellas, que cuando se llaman todavía pueden definir un vehículo dentro de la clase de vehículo. Reduce los guiones largos y la complejidad de los guiones. Es probable que descubra que tiene scripts de Door aquí y allá que pueden simplificarse con una clase de Door. Interfaz / Resumen / Herencia / Virtual se puede utilizar para modificar la definición de la clase Puerta en algunos scripts de la clase Vehículo.

También es más probable que tenga menos tiempo de desarrollo de esta manera. Una vez tuve el blog ASP.NET C # que usa muchas clases parciales. Había luchado con eso porque había demasiadas clases parciales y no sabes dónde están. Es como tratar con la lógica GOTO en la programación. ¡¡Malo malo!! Nunca pude crear un parche para la corrección de errores con éxito, ni pude personalizar el script porque estaba demasiado enterrado en una pila de clases parciales (así que haga algunas palabras renombradas).

Solo lo digo como un pensamiento de 1 centavo de mi mente.

@ fletchsod-developer: Algunas situaciones se manejan correctamente por herencia, pero no todas. Y algunos desarrolladores pueden hacer un mal uso de clases parciales, pero no todas. Hay algunas situaciones en las que encuentro las clases parciales muy, muy útiles, pero si no te gustan, no tienes que usarlas.

@ jfrank14 , como sugirió anteriormente @basarat, ¿el # 9 no funcionaría para usted?

// File1.ts
class Dog { 
 ...
}

// File2.ts
extends class Dog {
  woof() { /* ... */ }
}

@NoelAbrahams , ¿podría acceder a las definiciones del archivo1 en el archivo pero no al revés? Eso está bien para mí siempre que sea independiente del orden de inclusión.

Realmente espero que consideren las clases parciales a la C # para TS ... para mí la única razón es la generación de código, que es la razón principal detrás de las clases parciales de C # ... actualmente estamos haciendo mucha generación de código TS, y esperamos hacer más y más ... actualmente tenemos que confiar en "//

+1 - Realmente podría usar esto para agregar funcionalidad a clases generadas automáticamente; no existe otra solución elegante en la que pueda pensar o sobre la que haya leído hasta la fecha ...

+1 - Clases generadas por código. Objetos de C # / Java serializados al cliente

¡Necesita esto también para fusionar clases generadas!

Esta sería una característica muy útil para permitir una combinación simple de código generado con código no generado.

+1

+1 - También lo necesitamos para clases generadas parcialmente, sería más elegante que la herencia que emite bastante código que no es necesario para este propósito.

+1

Me encontré con esta limitación nuevamente, ¡agregue esta función! : +1:

+1

+1

+1

+1

La gente hace +1 aleatoriamente en esto no lo va a mover.

Los casos de uso hasta ahora son:

  • Tengo clases realmente grandes que abarcan varios archivos. @RyanCavanaugh indicó que probablemente era un problema de diseño importante con un código tan complejo y no suficiente para justificar la complejidad de la implementación.
  • Les gusta.
  • Otro argumento parece ser que C # los tiene.
  • El otro argumento tiene que ver con el código generado en otros lenguajes.

El segundo y tercer caso de uso no es convincente y el cuarto, sospecho, necesita cierto nivel de exposición de cómo entregar el n. ° 9 no cumpliría con un requisito muy similar e incluso los decoradores (n. ° 2249).

Parece que el n. ° 9 lograría lo mismo y sería aún más general (porque podría extender clases distintas a las suyas). Pero podría ser razonable querer el contenido de la clase en dos archivos (quizás porque uno está escrito a mano y el otro generado por una plantilla), y decir que ambos archivos definen los miembros esenciales de la clase con la misma importancia, y no requieren que uno el archivo "extiende" la clase define en el otro.

Es una diferencia casi existencial, pero quizás importante para algunas personas en algunas situaciones.

No quiero parciales en las clases por las razones indicadas, pero en los módulos tiene sentido en mi humilde opinión.

https://github.com/Microsoft/TypeScript/issues/447

Las clases parciales son extremadamente útiles para especializar casos de uso. No es una violación de ningún patrón de programación orientada a objetos, sino una forma de organizar el documento de código, que proporciona:

  1. Posibilidad de dividir la definición de clase / tipo en varios archivos. Para archivos de código grandes, es la característica de mantenibilidad ++.
  2. Oportunidad para que el compilador / sistema de compilación haga uso del enlace condicional en tiempo de compilación. Analogy MSBuild, caso de uso concreto en C #: DBML, clases de diseñador e incluso cosas más sofisticadas como https://github.com/dotnet/corefx/pull/2045/files.
  3. Protección inherente, por lo que dos (o más) desarrolladores no se encuentran durante el registro del código. ¡La resolución de conflictos golpea! :)
  4. Le permite generar código en la clase parcial principal y luego crear otro parcial para sobrecargas personalizadas. ¡Funciona perfectamente!

Creo en la separación de la lógica empresarial y las capas de datos en clases separadas y utilizo principalmente métodos estáticos en estas capas. Realmente no veo cómo podría ser más código y menos mantenible.

Si bien el método de extensión es una característica central del lenguaje influenciada por los patrones OO y se procesará en tiempo de compilación, las clases parciales se ocupan del diseño del documento / código que se coserá en la etapa de precompilación. A pesar de las similitudes en clases parciales, clases abstractas y métodos de extensión, existe una clara distinción.

: +1: para llevar las "clases parciales" inofensivas a TypeScript.

Mi caso de uso para las clases parciales es para la generación de código: mi equipo debe poder generar automáticamente parte de la clase en un archivo y agregar métodos y propiedades personalizados a mano en otro archivo. Estoy un poco confundido por qué @RyanCavanaugh "no está seguro de que ese tipo de situaciones se apliquen en JavaScript / TypeScript", ya que este caso de uso no está relacionado con el idioma elegido.

Ni los decoradores ni los métodos de extensión resuelven el problema de la generación de código con tanta elegancia como las clases parciales, y cada uno tiene importantes complejidades adicionales.

+1

+1

+1 para fines de generación de código.

En el entorno C #, la integración de un transpilador de clases C # a clases TypeScript eliminaría la necesidad de mantener dos definiciones diferentes de clases entre el servidor y el lado del cliente, ya que el lado del cliente, también conocido como clase TypeScript, se generaría automáticamente y la clase parcial que este problema está solicitando permitiría la personalización cuando sea necesario mientras aún se desarrolla en TypeScript nativo.

+1 para escenarios de generación de código

+1 extremadamente útil para la generación de código

¿Por qué no utilizar la herencia para la generación de código?

La herencia es más desordenada y tiene más gastos generales.

El lunes 10 de agosto de 2015 a las 10:09 a.m., Gorgi Kosev [email protected] escribió:

¿Por qué no utilizar la herencia para la generación de código?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129468291
.

La herencia crea acoplamientos de nombres inútiles

La herencia es menos asombrosa porque:

  1. Profundiza innecesariamente la cadena de prototipos y ralentiza la instanciación,
    aunque eso no es un gran problema para la mayoría de las aplicaciones, probablemente todo menos los juegos;
  2. Por falta de resumen y anulación, pierde algún tipo de seguridad o
    introduce la necesidad de algunos patrones desagradables como lanzar entre los dos
    tipos;
  3. Es confuso en general, ya que tener una clase básica comunica la idea de que
    se puede extender a otras clases, lo que rara vez es el caso con codegen

No digo que el mecanografiado TIENE que tener clases parciales para admitir codegen, solo
que las clases parciales son mucho mejores que la herencia o
composición.

@JoshMcCullough La sobrecarga de herencia en JS es mínima, y ​​no veo cómo es más desordenada que las clases parciales: con esas, puedes potencialmente dividir una clase en 10 archivos y perseguir tus métodos por todas partes.

@ yahiko00 ¿

@hdachev

  1. Hay algo de sobrecarga, pero es bastante mínima , solo se nota si está haciendo cosas que se traducen directamente en un par de instrucciones de la máquina.
  2. Buen punto: no obtiene la clase extendida cuando tiene que hacer referencia a la clase generada de otras clases generadas.
  3. De hecho, esas clases están destinadas a ampliarse; de ​​lo contrario, podría generarlas y terminar, ¿verdad?

AFAIK, esto no está en el radar ES7 +, ¿verdad? Eso podría ser problemático ...

En cualquier caso, adjuntar cosas adicionales al prototipo de una clase existente es bastante trivial en JavaScript y no es nada infrecuente, por lo que podría ser una buena idea tener una sintaxis para eso ...

Es sencillo en TS, pero el JS detrás de escena duplicaría el
"creación de prototipos" si utiliza clases base para todos los datos generados
objetos. Probablemente no tenga un impacto serio en el rendimiento a menos que tenga un montón de
objetos de datos, pero sigue siendo innecesario cuando sabemos que "parcial
clases "son posibles.

El lunes 10 de agosto de 2015 a las 11:03 a. M. Gorgi Kosev [email protected]
escribió:

@JoshMcCullough https://github.com/JoshMcCullough Sobrecarga de herencia
en JS es mínimo, y no veo cómo es más desordenado que las clases parciales:
con aquellos que potencialmente puede dividir una clase en 10 archivos y perseguir su
métodos por todo el lugar.

@ yahiko00 https://github.com/yahiko00 ¿Qué tipo de "nombre inútil
acoplamiento "¿de lo que estamos hablando aquí? ¿Se refiere a las cosas que necesitaría
qué hacer para obtener el n. ° 2 a continuación (codifique el nombre de la clase extendida)?

class MyClass extiende GeneratedMyClass {...}

@hdachev https://github.com/hdachev

1.

Hay algo de sobrecarga, pero es bastante mínima.
https://jsperf.com/prototype-chain-vs-direct-calls - solo perceptible
si está haciendo cosas que se traducen directamente a un par de máquinas
instrucciones
2.

Buen punto: no obtienes la clase extendida cuando tienes que hacerlo
hacer referencia a la clase generada de otras clases generadas.
3.

De hecho, esas clases están destinadas a ampliarse; de ​​lo contrario, podría
generarlos y terminar con eso, ¿verdad?

AFAIK, esto no está en el radar ES7 +, ¿verdad?

En cualquier caso, adjuntar elementos adicionales al prototipo de una clase existente
es bastante trivial en JavaScript y no es nada infrecuente, por lo que podría ser una
buena idea tener sintaxis para eso ...

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129483174
.

@ Davidhanson90 ese archivo no tiene clases. ¿Querías publicar esto en un hilo diferente?

Publicaré en el hilo correcto. https://github.com/Microsoft/TypeScript/issues/447

+1

+1

+100

+1

+1

+1 para generación de código.
Me he estado metiendo en angularjs y webapi y quiero hacer una herramienta que esencialmente cree definiciones JS automáticas a partir de mis objetos y servicios de c #. Luego, quiero poder extender estas clases sin tener que editar las definiciones de JS "andamios". He visto que algunas otras personas están solicitando esto y parece que tenemos casos de uso similares.

+1 para generación de código

+1 para la generación de código, angular tiene mucho código repetitivo, ya estamos generando mucho, pero con clases parciales podríamos hacer mucho más.

+1 una vez más para la generación de código. Intentar extender clases en otro archivo es complicado en el mejor de los casos.

@RyanCavanaugh , ¿qué se necesita para sacar esto del modo "+1" y avanzar en una dirección productiva?

-1 una siguiente sintaxis es mejor para mixins: # 2919

No estoy seguro de por qué no podemos tener clases parciales y mixins; dos características totalmente ajenas.

+1

Esto sería bueno cuando se genera el código, pero no desea modificarlo para agregar cosas al código generado.

+1

Tengo una propuesta que debería satisfacer ambos casos de uso:

  1. Quieres escribir métodos de tu propia clase en diferentes lugares.
  2. Desea agregar nuevos métodos a la clase de otra persona, por ejemplo, un archivo incorporado.

Para (2), normalmente solo usaría el operador :: para obtener la sintaxis infija.

Pero si desea que una clase antigua sea parte de una nueva interfaz, o si desea distribuir dinámicamente, necesitará modificar el prototipo.

interface ITimes {
    times(n: number): number
}

Array implements ITimes {
    times(n: number): number {
        return this.length * n
    }
}

// The compiler should check that all methods of ITimes and IOther are implemented.
Number implements ITimes, IOther {
    times(n: number): number {
        return this * n
    }

    other(): void {}
}

// The old types should typecheck with the new interface.
const x: ITimes = true ? [] : 0
x.times(3)

// The interface list can be empty. This essentially gives you partial classes.
MyClass implements {
    anotherMethod(): void {}
}

(En el ejemplo, utilizo nombres de métodos de cadena, pero dado que estos son tipos integrados, sería mejor usar símbolos una vez que se puedan verificar).

Una mejora que esto tiene sobre las clases parciales verdaderas es que la clase tiene una definición única y otros lugares simplemente la amplían. Esto significa que hay un lugar claro para importar la clase y facilita la traducción a JS.

: +1: Me encantaría ver clases parciales también.

¿Cuál es el estado de esta función?

Este caso de uso, por ejemplo:

Tengo un paquete Math que define una clase Set , con métodos Set#add , 'Set # remove'

// math.ts
export partial class Set {
  add(){}
  remove(){}
}

Tengo un paquete Relational opcional y pesado que define las clases Relation y Tuple .

Este paquete Relational también agregaría un método Set#product .

// relational.ts
///<reference path="./../math/tsd.d.ts"/>
partial class Set {
  product(){}
}

export class Relation(){}
export class Tuple(){}

Las clases parciales me permitirían componer clases de una manera más flexible. Si no quiero usar el paquete pesado relational , aún puedo usar la funcionalidad básica de Set. Otras clases partial solo entrarán y agregarán funcionalidad adicional a la clase.

Me estoy rascando la cabeza para hacer algo que tendrá una buena API sin el uso de parciales en este caso.

Todo lo que puedo traer es un patrón de repositorio.

// Without partials

// base

export class Base {
    static registerOperation(opName, method){
        this.prototype[opName] = method;
    }
    operation(name, ...args){
        return this[name].apply(this, args);
    }
}

// math.ts
import {Base} from './base';
class Set extends Base{
    // Define the methods here
    add(){}
    remove(){}
}

// Or here
Set.registerOperation("add", function(...){});
Set.registerOperation("remove", function(...){});

// relational.ts
import {Set} from './../math/math';

Set.registerOperation("product", function(...){});

// app.ts

import {Set} from 'math';

var set = new Set();
set.add // compiler error
set.remove // compiler error
set.product // compiler error

// have to use something like
set.operation("add", args);
// or
(<any>set).add(arg);

+1 Estoy usando t4 para generar clases y proxies a través de WebApi. Creo que la autogeneración se usa mucho en mecanografiado y cuando se trata de autogeneración, ¡las clases parciales son necesarias!

Sería genial para dividir aspectos de la misma clase, especialmente en React
+1

+1 Generación de código y "separación suficiente" en el caso de objetos de datos reutilizables puros con atributos específicos del cliente para una validación similar que sería bueno tener definidos en la clase pero mantenidos en una pieza separada del código fuente.

+1 para la generación de código de clases parciales y miembros adicionales de código manual para estas clases en archivos separados.

+1

+1 Tengo una gran base de métodos en nuestro servicio js api, sería mejor dividirlos en archivos, lo que facilita la comprensión de cada método.

api como fb (facebook) o gq (google analytics) , donde se le proporciona una clase u objeto global, que puede utilizar durante todo su desarrollo.

+1

Esta característica también sería una gran ventaja para nosotros.

Desarrollamos un middleware donde todo tipo de clientes pueden conectarse y comunicarse con nuestros servidores a través de él.

Generamos parte del código del cliente en función de lo que exponen los servidores.

Hasta ahora todo estaba bien. Pero ahora, nos gustaría poder transferir colecciones heterogéneas de objetos (pero con una misma clase base). Los objetos transferidos son parte del código generado en base a la API del servidor.

Para poder utilizar el poder de la herencia, los métodos abstractos son la clave en este caso. Pero nuestros desarrolladores no agregarán métodos al código generado, supongo que todos saben por qué;)

Por lo que entendí (soy un desarrollador de C #), la solución propuesta aquí (https://github.com/Microsoft/TypeScript/issues/9) no nos permitiría hacer eso.

Entonces, las clases parciales serían perfectas para nosotros. Generaríamos la clase como parcial, y si los desarrolladores necesitan agregar lógica o métodos, solo tendrían que escribir otra parte donde quieran.

+1

+1 muy útil para la generación de código

Me pregunto si el aumento de módulos básicamente niega la necesidad de esto como una característica específica.

Si tiene una clase generada, debería poder hacer algo como ...

import {MyClass} from "./MyClass.generated"

MyClass.prototype.partialMethod1 = function() {
  return true;
}
MyClass.prototype.partialMethod2 = function(abc: string) {
  this.doSomething(abc);
}

declare module './MyClass.generated' {
  interface MyClass {
    partialMethod1(): boolean;
    partialMethod2(abc: string): void;
  }
}

@disshishkov Parece una buena forma de implementarlo bajo la cubierta, pero aún así me gustaría un soporte de primera clase adecuado en el lenguaje sin la necesidad de modificar manualmente los prototipos y mantener esa interfaz.

Estoy de acuerdo con Elephant-Vessel. La parte creada manualmente de la clase parcial debe estar acoplada lo más libremente posible (en tiempo de diseño en TS) de la parte generada.

La sugerencia de @ david-driscoll funciona para muchos casos. Sin embargo, hasta donde yo sé, los prototipos no tienen acceso a variables internas. Esto limita la utilidad de este enfoque en contextos de generación de código donde desea generar las partes centrales de sus clases y luego aumentarlas con código personalizado.

+1

@ david-driscoll que parece una excelente manera de desugar esta función. (necesitaría agregar la capacidad de acceder a propiedades privadas / protegidas). Me pregunto qué pasaría con la declaración de clases parciales múltiples. ¿Cuál sería la visibilidad mutua de aquellos?

++ 1 La falta de parcial realmente me está perjudicando cuando trato de extender mis modelos generados automáticamente.

Creo que lo único en el alcance aquí es básicamente lo que propuso @ david-driscoll: podría declarar nuevos métodos (que se colocarían en el prototipo) y nuevas propiedades _no inicializadas_ (que no tienen codegen), pero no nuevas propiedades inicializadas (porque tienen efectos secundarios en el constructor codegen).

votado en contra, perdón por mi francés, las clases parciales son un intento estúpido de dividir tus clases divinas, que son inevitables en OOP, en varios archivos para que sean más fáciles de administrar (aunque siguen siendo clases divinas)

con OOP no hay futuro para ustedes (he estado allí, saben de lo que estoy hablando)

ven a FP, tenemos cookies y una forma de escribir un programa sin una sola clase y una mierda como esta

Así que básicamente ya permitimos esto de todos modos a través de interface A { method(): void } A.prototype.method = function() { }; ; codificar esto en un azúcar exactamente para eso tiene sentido.

Una cosa para bikeshed es la palabra clave partial . Una breve viñeta histórica: en C #, partial class originalmente iba a ser extension class (que pones en todas menos una declaración), pero no había una distinción clara sobre qué declaraciones serían las extension y qué declaración sería la declaración de no extensión. En su lugar, tiene el modificador partial que debe colocar en _todas_ las clases.

Este _no_ es el caso en TypeScript; más bien al contrario. Aquí, sólo una clase (llamémosla "declaración _primary_") puede tener constructores / campos de miembros inicializados; la declaración primaria no recibe ningún modificador nuevo. Todas las demás declaraciones (llamémoslas "declaraciones de _extensión_") solo pueden tener estáticas, métodos y campos no inicializados, y _necesitarás_ tener algún modificador en ellos para indicar que no son primarios.

La mejor suposición actual es

class Foo {
  x = 6; // only legal to initialize here
  constructor() { } // only legal to have constructor here
  someMethod() { }
}

// vvvvvvvvv thoughts?
   extension class Foo {
     someOtherMethod() {
     }
   }

: destellos:: bicicleta:: casa:: destellos:

Bikeshedding o no, esto suena horrible como el # 311 que fue abandonado debido a que probablemente interfiera con los estándares futuros de ECMAScript. ¿Por qué vale la pena considerar esto, pero el soporte de mixin adecuado no lo es?

¿El uso de la palabra clave extension entra en conflicto con los métodos de extensión ? ¿O esto negará completamente los métodos de extensión?

@kitsonk Creo que la propuesta de mixin tiene muchas más preguntas abiertas. La propuesta de clase parcial aquí es solo una codificación de cosas que ya están permitidas en TypeScript, solo con algo de azúcar sintáctico (esta característica podría literalmente hacerse como una reasignación sintáctica a cosas que ya tenemos).

@ Elephant-Vessel No creo que los métodos de extensión estén sucediendo si ocurren clases parciales. Las clases son el único lugar donde los métodos de extensión tienen sentido dadas nuestras restricciones, y el operador de enlace ES7 + propuesto es una mejor manera de calzar en sintaxis similar a una extensión.

@RyanCavanaugh : para mí, parece un poco restrictivo exigir que una clase sea primaria y las otras no. Si desea particionar su código de modo que un archivo contenga todos los métodos de una clase y el otro contenga todas las propiedades, ninguno parece más obviamente primario, y ¿por qué debería verse obligado a elegir? ¿No es más limpio tener parciales donde todos son iguales en importancia y fusionados por el compilador?

@RyanCavanaugh ... Lo haría exactamente de la misma manera que lo hace c # ... funciona muy bien para la generación de código ... que creo que era el problema principal que la función debía resolver en c # ... aunque mi memoria es borrosa ... nunca tuve ningún problema, ha existido durante años y está probado en batalla ...

Si las clases parciales / extendidas no pueden tener campos inicializados, creo que sería muy bueno admitir algo como métodos parciales que conocemos de C # , básicamente un contrato ligero específico para clases parciales. Asi como:

public partial class MyGeneratedClass
 {
     partial void Initialize();
     public constructor()
     {
          //...
         this.Initialize();
    }
 }

 public partial class MyGeneratedClass
 {
     partial void Initialize()  { //... }
 }

La necesidad de poder parcializar / extender clases de terceros podría resolverse con bastante facilidad (?) Haciendo que partial/extends generalmente sea opcional pero necesario para el uso de métodos parciales.

Creo que esto podría aportar un gran poder expresivo y operativo al concepto que queremos aquí.

Aquellos que buscan mixins.

Considere la propuesta de inicialización del

function enableBeingPositioned<a>(
   // hypothetical syntax
   something: a /* <-- before type */ => a & { x: number; y: number; } /* <-- after type */
): void { 
   something.x = 0;
   something.y = 0;
}
let value = {};
value.x; // <-- should not typecheck
enableBeingPositioned(value);
value.x; // <-- should typecheck

Los lenguajes como Java no ofrecen clases / estructuras parciales. Es una característica de nicho de C #. Imitar los conceptos de "clase / estructura parcial" de C # en su totalidad es la mejor manera de hacerlo.
En el paso de precompilación, lo primero que podría hacer el compilador de TS es unir los bloques de código parciales en la memoria y luego pasar a la ruta de la canalización de compilación normal. Esto será más que suficiente para calificar para la función v1.0.0-preview1.0000 de clases parciales en TypeScript.
Cualquier cosa más allá de lo que ofrece C # es algo que se puede introducir en versiones posteriores cuando la función evolucione y supere su versión de prueba / vista previa. Los casos de las esquinas y el tipo de cosas oscuras similares se pueden discutir por separado uno a la vez y podemos preocuparnos por el ajuste y el acabado más tarde ... IMO.

También estoy de acuerdo en hacer clases parciales y de extensión exactamente de la manera c #
¿Es la mejor manera de hacerlo en términos de sintaxis y semántica, ambos funcionan
extremadamente bien para sus respectivos casos de uso. Las clases parciales mejoran el
usabilidad del código generado. Los métodos de extensión mejoran la usabilidad
de métodos estáticos que operan en interfaces, genéricos especificados y terceros
tipos de fiesta. Ambos son realmente geniales, pero recuerde que no son los
misma característica, digo esto aquí debido a la sintaxis propuesta anteriormente.

Los métodos de extensión le permitirían a uno hacer esto (preocuparse por la falta de formato,
escribiendo en un teléfono):

extender MyType [] {
somethingSpecificToMyType () {...}
}

extender WeirdThirdPartyDatastructure{
aquí igual() { ... }
}

Que, entre otras cosas, como poder usar bibliotecas como subrayado
sin el cruft, lo que básicamente significa cosechar los beneficios sintácticos de
extendiendo los tipos incorporados sin realmente contaminarlos.

Tan breve, no tratemos a los dos como uno y lo mismo, ambos son muy
genial pero no lo mismo. Personalmente, me encantaría tener ambos en nuestro
idioma.

Por cierto, como nota al margen, imagino que mucha gente se avergüenza ante la idea de
generación de código y están listos para explicarles a todos que no tiene lugar
en javascript porque hay formas más idiomáticas de lograr lo mismo.
Hay dos formas en las que la generación de código es de gran ayuda en algunos
proyectos, como el que estoy trabajando en este momento.

  • En proyectos sensibles al rendimiento, como juegos, en los que simplemente
    todo para maximizar el presupuesto de su CPU con un trabajo útil, codegen ayuda mucho
    para generar código que sea simple de razonar y altamente optimizable
    / código monomórfico que encaja muy bien. Por ejemplo, en un
    motor de juego de entidad / componente, el código generado puede ayudar con un montón de cosas
    como un código repetitivo muy rápido para unir componentes, evento
    despacho, etc.
  • En las aplicaciones de grandes empresas, el código generado ayuda a aprovechar al máximo
    el sistema de tipos, por ejemplo mediante la emisión de métodos con muy específicos
    firmas para consultas de bases de datos, etc., lo que maximiza el tiempo de compilación
    verifica, ayuda muchísimo con la refactorización y también simplifica enormemente la depuración
    porque le permite recorrer un código de orquestación muy simple.
  • El más importante es cuando mantiene algún componente sincronizado entre
    entornos de múltiples idiomas, por ejemplo, algo relacionado con la mensajería
    como búferes de protocolo, o simplemente interfaces de mensajes json, etc.

Como otro nodo lateral en beneficio de cualquiera que se pregunte qué parcial
las clases tienen que ver con codegen, el valor de mantener las partes generadas
de una clase en un archivo separado proviene de los requisitos de control de fuente:
el material generado suele ser un artefacto de construcción que cambia rápidamente y usted
no quiero que se comprometa en su repositorio.

Me gusta la idea de implementar esto de acuerdo con un modelo bien conocido y probado de este concepto, al igual que lo que tiene C #. La "imitación" tiene algunas connotaciones negativas, pero tiene un valor significativo si se hace de manera inteligente. Por otro lado, la parte 'inteligente' de eso es ser consciente de las diferentes circunstancias y limitaciones, por lo que no protestaré si los parciales para TypeScript no se ven exactamente como los parciales para C #.

@ Elephant-Vessel, estoy de acuerdo en que no es necesario que sea una copia ídem de los parciales de C #, el diseño general se puede diseñar primero en torno al sabor TypeScript / JavaScript mientras se inspira al máximo en los parciales de C #. Mi sugerencia es ir con la palabra clave 'registrar partial ' y 'coser parciales' como primer paso, y enviarlo como una función experimental / de vista previa (para que el consumidor no comience a depender de él en el código de producción correctamente De buenas a primeras). Más tarde, según la respuesta de la comunidad, evolucione la función hasta que esté lista para la producción y para RTM. Si nos preocupamos por todo tipo de trampas y escenarios complicados de antemano, lo más probable es que demore aún más el asunto.

+1: para ampliar y mantener los objetos generados.

+1 para mantener el código generado

Creé polyfill y dividí mi gran clase.

Defina una clase como interfaz.

export class C {
  constructor() {
  }
}
export interface C {
  m(): void;
}

Implementar miembros de la clase.

export default class extends C {
  m(): void {
  }
}

Fusionar implementaciones.

import {C} from './core';
import m from './member/m';

compose(C, m);
export {C}
import {assign} from './assign';
import {concat} from './concat';

export function compose<T extends new (...args: any[]) => any>(target: T, ...sources: T[]): T {
  return concat([target], sources)
    .reduce((b, d) => {
      void assign(b.prototype, d.prototype);
      for (const p in d) if (d.hasOwnProperty(p)) b[p] = d[p];
      return b;
    });
}

https://github.com/falsandtru/spica/commit/a6ff30da5319db5f25f703a29da48fc0f7dbe2fe

Creo que esta es una idea terrible por una razón específica: empeorará significativamente las reglas ya complicadas para los problemas de herencia dependientes del orden de declaración global, ambiental, externo, de espacio de nombres y quebradizo. Esto no es C # y la resolución de nombres y las declaraciones de miembros es muy diferente.

Quizás use un objeto o espacio de nombres en lugar de una clase, como el patrón de módulo revelador si realmente necesita hacer esto. De lo contrario, su clase probablemente sea demasiado grande.

También se pueden utilizar decoradores para implementar asociaciones entre el código generado y el código escrito a mano.

¿Tienes otras soluciones?

Me encantaría ver esto agregado a TS también

+1 para fines de generación de código (DTO de una referencia de servicio WCF con definiciones de clase parciales adicionales)

+1 para fines de generación de código. Mi caso de uso es: tengo componentes React cuyas funciones render() se generan externamente a través de react-templates .

Con las clases partial , podría verificar dichas funciones con la clase principal (la Component ). Sin clases parciales, lo único que puedo hacer es vincular la función a la clase principal y esperar lo mejor.

Con el próximo TS 2.0, estaba pensando que algunos casos de generación de código podrían cubrirse con la función de tipo this .

Por ejemplo, en el caso que describí anteriormente, en lugar de

partial class MyComponent extends React.Component<any,any> {
    render() {
        ...
    }
}

puedo escribir

function render<this extends MyComponent>()
        ...
}
MyComponent.prototype.render = render;

+1 Como desarrollador, quiero clases parciales para que varias plantillas puedan generar / modificar la misma clase distribuida en varios archivos.

Mis comentarios se referían específicamente a las clases parciales, no a los mixins. Los dos son fundamentalmente diferentes. Las clases parciales tratan de dividir el código físico, los mixins tratan de dividir el comportamiento lógico en rasgos reutilizables que enriquecen otros objetos tanto a nivel de tipo como de valor.
No creo que las clases parciales sean una buena idea para TypeScript. Los mixins, por otro lado, son una buena idea, ya que brindan una mayor expresividad y se combinan extremadamente bien con el sistema de tipos de TypeScript y los modismos de JavaScript, como lo señaló @ aleksey-bykov.

@wendellm, ¿ se le ocurre una forma limpia de hacer esto dado que los módulos ES son físicos? Las clases parciales funcionan bien y tienen mucho sentido en un lenguaje como C #, donde los módulos son lógicos, no físicos. Desde el punto de vista del CLR, los espacios de nombres no existen, es solo que los nombres de las clases pueden tener puntos (fuente una entrevista con @ahejlsberg)

+1 ¡Necesito clase parcial!

¿Podemos ampliar la interfaz? La interfaz también puede tener algunas funciones implementadas, como Swift:

interface Rect {
    x: number
    y: number
}

extension Rect {
    area() => this.x * this.y
}

Versión rápida:

protocol Rect {
    var x: Float {get}
    var y: Float {get}
}
extension Rect {
    func area() -> Float {
        return self.x * self.y
    }
}

+1

La generación de código y las clases parciales van de la mano.

La herencia es una mala solución a lo que es esencialmente una directiva #include glorificada.

Tome Angular 2 por ejemplo. No leerá metadatos de la clase principal, lo que hace que la extensión por herencia no sea práctica.

@tsvetomir es un problema de Angular, no de TypeScript.

La herencia es una mala solución para lo que es esencialmente una directiva #include glorificada

Sí, la herencia es una mala solución, pero el problema es usar clases en primer lugar. Tienen su lugar pero es muy limitado. Las clases de JavaScript son bastante débiles e inexpresivas en comparación con las clases en otros idiomas.

Mecanografiado no es un lenguaje "Angular" ... Nunca lo fue

El 23 de julio de 2016, a las 9:46 p.m., Aluan Haddad < [email protected] [email protected] > escribió:

@tsvet omirhttps: //github.com/tsvetomir ese es un problema de Angular, no de TypeScript.

La herencia es una mala solución para lo que es esencialmente una directiva #include glorificada

Sí, la herencia es una mala solución, pero el problema es usar clases en primer lugar. Tienen su lugar pero es muy limitado. Las clases de JavaScript son bastante débiles e inexpresivas en comparación con las clases en otros idiomas.

Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en Gi silencie el th

No veo el problema como específico de Angular de ninguna manera. Es un caso particular en el que las clases parciales pueden haber ayudado.

La solicitud se origina en experiencias pasadas con C # y la falta de características familiares. La comparación es inevitable ya que TypeScript no hace ningún esfuerzo por distanciarse de .NET. Los usuarios esperan alguna forma de paridad de características.

La solicitud se origina en experiencias pasadas con C # y la falta de características familiares. La comparación es inevitable ya que TypeScript no hace ningún esfuerzo por distanciarse de .NET. Los usuarios esperan alguna forma de paridad de características.

@tsvetomir como desarrollador de .NET desde hace mucho tiempo, y como programador apasionado de C #, fundamentalmente no estoy de acuerdo.

El artículo que citó no es fidedigno y no refleja ningún material oficial del equipo de TypeScript.

Además, contradice directamente la filosofía de diseño de TypeScript según lo declarado por @ahejlsberg en numerosas charlas, entrevistas y publicaciones.

Además, el punto de vista del artículo citado refleja un malentendido fundamental en cuanto a la naturaleza de TypeScript.
Irónicamente, los puntos en común que C # tiene con TypeScript son en realidad los mismos que tiene con JavaScript.
Estos puntos en común no son la programación basada en clases, sino más bien construcciones como cierres y funciones de orden superior.

C # o no, necesitamos clases parciales que ayudarán a TypeScript a acercarse a las características del prototipo de Javascript.

@ yahiko00
TypeScript ya tiene todas las funciones de prototipo de JavaScript.
Es un superconjunto.

Hay varios problemas con la noción de clases parciales en TypeScript, que incluyen

  1. A diferencia de C #, las definiciones de clase de TypeScript (JavaScript) son imperativas, no declarativas. Parecen declarativos, pero no lo son. Esto significa que dependen de un orden de ejecución determinista.
  2. El sistema del módulo ECMAScript se basa en la importación de unidades de código físicas, no lógicas.
    Por ejemplo, digamos que tengo

_app / my-class-part-1.ts_

TypeScript export partial class MyClass { firstName = "John"; lastName = "Smith"; }

y
_app / my-class-part-2.ts_

TypeScript export partial class MyClass { fullName = this.firstName + ' ' + this.lastName; }

¿Cómo puedo importar esta clase? Como no puedo importar desde ninguno de los módulos, imaginemos una abstracción hipotética en TypeScript que me permita escribir
_app / main.ts_

TypeScript import { MyClass } from './my-class';

¿Qué significaría esto? Incluso si el compilador de TypeScript pudiera inferir que _app / my-class-part-1.ts_ debe cargarse antes que _app / my-class-part-2.ts_, no puede invalidar la importación de las partes individuales ni puede garantizar se importan en el orden correcto mediante un cargador de módulo asíncrono como RequireJS o lo que eventualmente se implementará en los navegadores.

Toda la noción de clases parciales está fundamentalmente en desacuerdo con el sistema de módulos ECMAScript.

Actualización: Necesitaría realizar una inferencia de dependencia arbitrariamente compleja y emitir un JavaScript muy extraño.

TypeScript ya tiene todas las funciones de prototipo de JavaScript.
Es un superconjunto.

Debería haberme expresado mejor. Por supuesto, todos sabemos que TypeScript es un superconjunto de JavaScript. Pero quería decir que las clases parciales ayudarían a las clases de TypeScript a acercarse a los prototipos de JS. Por el momento, podemos tener clases "estáticas" y no extensibles, mientras que, con un enfoque de prototipo, podemos extender funciones de tipo clase.

Importar clases parciales llevaría a extender la sintaxis actual de ES6, estoy de acuerdo. Podríamos imaginar algo como esto:

import { MyClass } from ['app/my-class-part-1', 'app/my-class-part-2'];

Las clases de TypeScript se basan en las clases de ECMAScript y, por tanto, adolecen de la falta de expresividad de esta última. Considere el uso de funciones, módulos, "espacios de nombres" u objetos simples para lograr lo que necesita.

Funciones es lo que queremos evitar con la noción de clase.
Los módulos o espacios de nombres brindan una extensibilidad simple y no tienen la misma expresividad que las clases de TypeScript. Además, no se puede instanciar un espacio de nombres o un módulo. No tienen el mismo propósito que las clases.

@aluanhaddad Veo a qué te refieres con respecto a los módulos. Una posible solución sería permitir solo uno de los archivos a export partial class mientras que el resto contiene solo partial class definiciones. Lo importa desde el lugar donde se exporta. De tu ejemplo:

_app / my-class.ts_

export partial class MyClass {
    firstName = "John";
    lastName = "Smith";
}

_app / my-class.part.ts_

partial class MyClass {
    get fullName(): string {
        return this.firstName + ' ' + this.lastName;
    }
}

_app / main.ts_

import { MyClass } from './my-class';

Lo que falta es una sintaxis para que el compilador sepa dónde ubicar las partes. Esto no es un problema con C # donde procesa todos los archivos a la vez. Esta podría ser una sintaxis especial, como la que se usa para la fusión de declaraciones .

No es necesario que las clases parciales permitan definiciones arbitrarias. Pueden limitarse solo a métodos, propiedades y constructores, por ejemplo. Esto hace que el orden de ejecución sea insignificante y permite que el compilador arroje un error si un miembro está duplicado.

No veo por qué el aumento de módulos no es válido para estos escenarios. Nos ha funcionado muy bien con Rxjs5.

Simplemente crea su clase base y luego, con la generación de su código, genera todos los métodos aumentados que necesita en la parte superior de la clase base. No importa si el código generado es bonito, se supone que ningún ser humano debe escribirlo.

¿Es un reemplazo perfecto para las clases parciales? Supongo que no, pero no veo que ocurran clases parciales a menos que ECMAScript decida que JavaScript las necesita. La semántica de cómo las clases parciales están vinculadas entre módulos externos me duele la cabeza.

@ david-driscoll para hacerlo aún mejor, los métodos agregados pueden tener un argumento this: declarado, para una verificación de tipos más sólida.

En el ejemplo citado de aumento de

// observable.ts stays the same
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
    interface Observable<T> {
        map<U>(f: (x: T) => U): Observable<U>;
    }
}
Observable.prototype.map = function (this: Observable, f) {
    // here "this" has the shape of the "Observable" class
}

Sí. Rxjs aún no lo usa silenciosamente, porque es una función 2.0 y 2.0 aún no se ha lanzado. Está en mi lista de resultados más tarde.

Veo lo que quieres decir con respecto a los módulos. Una posible solución sería permitir que solo uno de los archivos exporte una clase parcial, mientras que el resto contiene solo definiciones de clase parciales. Lo importa desde el lugar donde se exporta. De tu ejemplo:

_app / my-class.ts_

export partial class MyClass {
    firstName = "John";
    lastName = "Smith";
}

app / my-class.part.ts

partial class MyClass {
    get fullName(): string {
        return this.firstName + ' ' + this.lastName;
    }
}

Me gusta el concepto sintáctico, pero el problema es que acaba de introducir un nuevo _ tipo_ de archivo fuente que se consume como una especie de módulo implícito, parece un script y debe cargarse de una manera específica.

Lo que falta es una sintaxis para que el compilador sepa dónde ubicar las partes. Esto no es un problema con C # donde procesa todos los archivos a la vez. Esta podría ser una sintaxis especial, como la que se usa para la fusión de declaraciones.

La combinación de declaraciones funciona para _declarations_, no para archivos de origen.

No es necesario que las clases parciales permitan definiciones arbitrarias. Pueden limitarse solo a métodos, propiedades y constructores, por ejemplo. Esto hace que el orden de ejecución sea insignificante y permite que el compilador arroje un error si un miembro está duplicado.

Todos estos _son_ comúnmente dependientes del orden.

Editar: Es decir que definirlos modifica la clase.

Probablemente alguien ya mencionó esto, pero otro caso útil es cuando genera parte de la clase a partir de algún generador de código, luego desea agregar manualmente más código directamente en su implementación.
Realmente no desea hacer clases de mezcla o derivadas.
Sigue siendo una clase, solo una parte se genera automáticamente y otra parte manualmente.

Por cierto: si alguien necesita generar tipos de TS a partir de clases C #, puede verificar TypeScriptBuilder

¡Gracias por considerar esto!

Voy a probar su TypeScriptBuilder;)

Mi sugerencia...

¿Qué pasa si el compilador no intenta encontrar todas las partes? ¿Y si tuviera que importar otras partes y controlar todo?

/MyClass.partial.ts

export partial class MyClass {
    firstName = "John";
    lastName = "Smith";
}

MyClass.ts

// v- this would NOT throw an error because the definition is marked as partial
import { MyClass } from "./MyClass.partial";

export class MyClass {
    get fullName(): string {
        return this.firstName + ' ' + this.lastName;
    }
}

Luego...

  1. Si quiero usar MyClass tengo que importarlo desde MyClass.ts
  2. Cuando se compila, MyClass.partial.ts generará un javascript que extiende MyClass.prototype como se mostró aquí antes. Pero exporta una función que recibe el prototipo a ampliar.
  3. MyClass.partial.ts se importa en MyClass después de que se haya definido MyClass y se haya llamado a la función de extensión.

En una nota al margen ... no hay nada que me impida generar el código compilado directamente. Pero pierdo toda la grandeza de Typecript.

@svallory Creo que definitivamente ese es el enfoque correcto aquí. Específicamente porque dice que el código generado exportará una función que causaría el aumento, funcionaría incluso con importaciones asincrónicas porque las funciones se pueden llamar en el orden determinista necesario.

De manera más general, y olvido el problema en el que se hizo referencia, si las asignaciones al prototipo de clase afectaban la forma de la clase, podría haber muchos beneficios. Esto mejoraría las bibliotecas de mezcla, haría que los decoradores fueran mucho más útiles y, en general, fomentaría menos jerarquías verticales.

Podría ser complicado para el compilador rastrear todo esto, pero probablemente podría funcionar siendo explícito como usted sugiere.

Sería una mejora del sistema de tipos, con el caso de uso de clases parciales cayendo fuera de esa mejora.

Lo hice así:

Lo hice como lo siguiente :
 archivo1 . ts
 interfaz ifoo {
 a ( ) : vacío ;
 }
 clase foo implementa ifoo {
 a ( ) { / * hacer algo * / }
 }
 archivo2 . ts
 /// <ruta de referencia = "file1.ts" /> // no es necesario
 interfaz ifoo {
 b ( ) : vacío ;
 }
 foo . prototipo . b = ( ) = > { / * hacer algo * / }
 archivo3 . ts
 /// <ruta de referencia = "file1.ts" /> // no es necesario
 interfaz ifoo {
 c ( ) : vacío ;
 }
 ( < ifoo > foo . prototipo ) . c = ( ) = > { / * hacer algo * / }
 consumidor . ts
 /// <ruta de referencia = "file1.ts" />
 /// <ruta de referencia = "file2.ts" />
 /// <ruta de referencia = "file3.ts" />
 sea ​​f = new foo ( ) ;
 f . a ( ) ;
 f . b ( ) ;
 f . c ( ) ;

El aumento de módulos todavía resuelve el 90% del problema aquí. Dados los ejemplos de @svallory .

/MyClass.partial.ts

export partial class MyClass {
    firstName = "John";
    lastName = "Smith";
}

MyClass.ts

// v- this would NOT throw an error because the definition is marked as partial
import { MyClass } from "./MyClass.partial";

export class MyClass {
    get fullName(): string {
        return this.firstName + ' ' + this.lastName;
    }
}

El aumento del módulo sería algo así como ...

MyClass.ts

export class MyClass {
    firstName = 'John';
    lastName = 'Smith';
}

MyClass.generated.ts

import { MyClass } from './test';

Object.defineProperty(MyClass.prototype, "fullName", {
    get(this:MyClass) {
        return this.firstName + ' ' + this.lastName;
    }
});

declare module './test' {
    interface MyClass {
        readonly fullName: string;
    }
}

Es un poco más detallado que una clase parcial, pero en realidad la clase parcial sería simplemente azúcar sintáctica para esta implementación.

Las únicas trampas que hoy no tienes serían:

  • No puede acceder a campos privados o protegidos usando la sintaxis this: MyClass .

    • Esto se puede resolver fácilmente en el código generado mediante la conversión a any .

    • Quizás esto sea algo que se pueda agregar, de modo que pueda acceder a miembros protegidos de this . Puede haber otras reglas en torno a eso.

  • El código generado parece un poco más intimidante, pero para el consumidor, una vez que se hayan aumentado las cosas, nunca notarán la diferencia.

EDITAR: Tenga en cuenta que esta es la sintaxis de TS 2.0+.

@ david-driscoll estoy de acuerdo. Creo que es más simple y me gusta que no introduce la sintaxis adicional. Como saben, me opongo a las clases parciales como una característica específica del lenguaje, pero creo que sería beneficioso si TypeScript rastreara las asignaciones a los prototipos en general y refinara la forma del objeto en consecuencia. Creo que su enfoque es la forma correcta de dividir clases en varios archivos.

@wongchichong Tu ejemplo usa nombres de clases globales y /// <reference path="..."/> . No creo que vaya a escalar bien, aunque podría usar un espacio de nombres para mejorar la situación.
El código escrito como módulos (es decir, módulos externos) es una bestia muy diferente porque involucra un cargador que necesita ser consciente de las dependencias que dependen del orden y son implícitas. Es por eso que la sugerencia de

¿Qué pasa si alguien no usa módulos?

Las clases parciales de

Para aclarar, quiero decir que son una mala idea en un lenguaje como JavaScript, donde las declaraciones de clase son imperativas, no declarativas. Incluso si está utilizando espacios de nombres, terminará dividiendo su código en varios archivos, lo que significa que tendrá que depender del orden de las etiquetas de secuencia de comandos de esos archivos para que esto funcione.

En un lenguaje como C #, las clases parciales funcionan bien, pero eso se debe a que son fundamentalmente diferentes de las clases en JavaScript.

Es importante recordar que en JavaScript, las declaraciones de clases ni siquiera son elevadas y mucho menos declarativas.

Salvo ES6, el concepto de "clase" lo define TS. JS no tiene noción de una clase _en absoluto_.
Entonces, depende de TypeScript decidir qué clase puede ser, ¿no?

Salvo ES6,

No podemos excluir ES6 cuando hablamos de las características del lenguaje TS, porque uno de los objetivos de TS es seguir las especificaciones ES actuales / futuras .

Eso es cierto :/

Para sistemas grandes con clases generadas por código, es decir, entidades, clientes de la capa de servicio, etc., clases parciales según el diseño de C #, brinde una solución muy elegante cuando el código generado necesita ser extendido con un estado y comportamiento adicional. En mi proyecto actual, donde tenemos ~ 500 clases de generación de código, extraño mucho esta característica.

Todavía no entiendo por qué algunas personas son tan hostiles contra una función que ha demostrado ser útil en otros lugares, excepto diciendo "es una mala idea".

Parece que esto no se ha discutido mucho en esdiscuss, el único hilo que encontré tiene 9 publicaciones.

@ yahiko00 No es que sea hostil al concepto. Como dices es muy útil en lenguajes como C #.

Como programador entusiasta de C #, encuentro que las clases parciales son muy útiles para ciertas tareas. Sin embargo, no interfieren con los otros aspectos del idioma. No rompen abstracciones relacionadas con la clave, como espacios de nombres, ensamblados y declaraciones de tipos ordenables arbitrariamente.

JavaScript, por otro lado, tiene un sistema de módulos que, en el mejor de los casos, es incipiente. Las clases son una característica nueva y, diría yo, aún no muy expresiva del idioma, necesitan tiempo para crecer.

Si damos un paso atrás por un momento y consideramos los métodos de extensión, otra característica poderosa y mucho más útil de C #, me encantaría verlos agregados a JavaScript, pero hay problemas fundamentales para hacerlo que aún no se han resuelto.

Cuando lleguemos a un punto en el que se puedan especificar clases parciales sin romper o restringir severamente conceptos mucho más fundamentales como los módulos, estaré a favor de agregarlos.

Dicho esto, como señala @SaschaNaz , esto _debe ser abordado en ECMAScript.

Tal vez cualquier persona interesada pueda abrir un nuevo hilo en http://esdiscuss.org y publicar la URL aquí para que podamos continuar la discusión allí 😄

PD: O en un discurso ES más parecido a GitHub.
PS2: O en WICG también similar a GitHub y más activo.

A mí también me encantaría ver alguna construcción que permita la generación de código para TS. Las clases parciales funcionan muy bien para esto en C #.

Tal vez me esté perdiendo algo, pero me parece que el problema principal es extender una clase con acceso a variables 'privadas' desde algún lugar que no sea en el constructor, preferiblemente desde otro archivo.

Según la discusión, parece que gran parte de la innovación TS 'segura' se ha realizado. Ahora solo estamos esperando ver qué hace el órgano de gobierno de ES porque no queremos romper nada en el futuro. Totalmente válido, pero un poco descorazonador porque esperaba un tren en movimiento más rápido con TS.

Un requisito previo es encontrar un buen desugaring :

Aunque anteriormente pensaba que las clases parciales eran bastante tontas porque simplemente podrías agregar a la clase directamente, puedo ver el atractivo dado que hacerlo implicará hacer que tus métodos no sean enumerables y corregir el súper enlace (el último de los cuales es actualmente imposible en ES6).

Primero tenemos que encontrar una buena desguarnecimiento de las clases en general en primitivas imperativas compositivas, como el antiguo método. Pero una vez que tengamos eso, agregar un poco más de azúcar en la parte superior como clases parciales podría ser razonable, dependiendo de cuán ergonómicas, o no, terminen siendo esas primitivas.

No estoy seguro de qué significa esto exactamente, pero probablemente así:

class A {
  foo() { return "foo" }
}
class B extends A {
}

partial(B, class {
  get foo() {
    return super.foo(); // should work
  }
});

(Cruz publicada en esdiscuss )

Parece que hay una forma bastante simple de implementar una clase parcial en JS puro (con ES2017 getOwnPropertyDescriptors ). La gente de ES puede pensar que un azúcar de sintaxis ni siquiera será necesario solo para eliminar este pequeño código.

function partial(base, extension) {
  extension.prototype.__proto__ = base.prototype.__proto__; // to enable 'super' reference
  const descriptors = Object.getOwnPropertyDescriptors(extension.prototype);
  delete descriptors.constructor; // must not override constructor
  Object.defineProperties(base.prototype, descriptors);
  
  return base;
}

Usar esto en TS requiere un interface duplicado para extender el tipo de clase existente, por supuesto.

@SaschaNaz Espero que las propiedades agregadas al constructor a través de getOwnPropertyDescriptors se oculten para la "autosugestión" del IDE (ya que se agregan en tiempo de ejecución). Recuerde, TypeScript nació como un JavaScript con "autosugestión" (a veces la gente lo llama "tipado fuerte", pero en realidad lo que les gusta es "autosugestión").

PD Por qué la gente necesita clases parciales: generación de código. Pero nuevamente, la generación de código debe ser soportada tanto por IDE ("herramienta" fácil de ajustar al archivo de origen, resultado jerárquico en el explorador de archivos, T4 o soporte similar de "generador de código común") y en el lenguaje (clases parciales, métodos de extensión). Solo después de comprender esto, es posible comprender lo que realmente preguntan los desarrolladores. Si no ve la imagen completa, por supuesto, las "clases parciales" parecen "una tontería".

@aluanhaddad : ¿pero la función principal de TypeScript no es proporcionarnos elementos de lenguaje que no están presentes en JavaScript? ¿Por qué es una mala idea dividir una clase en dos archivos TypeScript parciales, que se compilarán en un solo archivo JavaScript?

Realmente no veo el problema de darle una herramienta a la comunidad. Y estoy 100% seguro de que habrá desarrolladores que lo abusarán. Pero también desarrolladores que saben exactamente cómo usarlo. ¿Pero no tenemos la misma situación con cualquier construcción de cualquier lenguaje?

+1 para fines de generación de código. Y estoy de acuerdo con @rpokrovskij , por qué elegimos TypeScript, también puedo usar JavsScript directamente, solo porque parece C # y tiene soporte para ide.

@greendimka No sé cómo podría funcionar esto sin divergir y agregar una complejidad sustancial a los módulos ES.

@ xiexin36 TypeScript seguro que me parece más a JavaScript que a C #.
Hay puntos en común entre JavaScript y C #, pero sus nociones de class son mundos aparte.

@ xiexin36 para la generación de código, aún puede usar el aumento de módulo (como señalé anteriormente en este hilo ). Sintácticamente no es tan "fácil" como las clases parciales, pero puede realizar la misma tarea.

@aluanhaddad Bueno, funciona de manera muy simple: las clases parciales no cambian la estructura del código de ninguna manera. Simplemente nos permiten escribir la definición de la misma clase en varios archivos (que de todos modos se compilan en un solo JS).

@aluanhaddad Estrictamente hablando: javascript no tiene un concepto de clases (las clases de ES6 solo proporcionan un azúcar sintético). Entonces, si intentamos seguir el concepto de tener TypeScript tan débil como JavaScript, ¿no deberíamos descartar las clases de TypeScript? ¿No deberíamos simplemente volver a JavaScript?

Las clases de
TypeScript no tiene como objetivo agregar construcciones adicionales al tiempo de ejecución de JavaScript.

Las clases de ES6 son _muy_ mediocres. Eso no significa que JavaScript no sea un lenguaje poderoso (lo es). TypeScript proporciona escritura estática para detectar errores en el momento del diseño, proporcionar herramientas de alta calidad, permitir una especificación extremadamente precisa y rigurosa de las interfaces de los componentes y, en general, mejorar la productividad.

Las clases de ES6 son _muy_ mediocres, pero que TypeScript introduzca su propia noción de clases sería una violación directa de sus objetivos de diseño.

Simplemente evite las clases y su problema se resolverá.

Bueno, funciona de manera muy simple: las clases parciales no cambian la estructura del código de ninguna manera. Simplemente nos permiten escribir la definición de la misma clase en varios archivos (que de todos modos se compilan en un solo JS).

¿Y romper casi todas las herramientas conocidas por el hombre? No gracias.

@ david-driscoll Gracias, lo intentaré, pero también uso Angular 2.0, me pregunto si genero código con
@Input o @Output , funcionará, de todos modos lo intentaré.

@aluanhaddad es una molestia ver cómo

Y por cierto, equipo de TypeScript, ¡danos funciones parciales también! Y dígale lo mismo al equipo de C #, ya que tenemos "funciones en funciones" por ahora. :)

Solo para asegurarse, ¿se da cuenta de que las funciones parciales tienen un significado muy específico y que lo que está describiendo no son funciones parciales por ningún tramo de la imaginación?
La generación de código es útil, yo también la uso.

@aluanhaddad . ¿Entiende el término "una unidad de compilación en varios archivos"? Javascript, por supuesto, no es un lenguaje de compilación, pero a) la función debe analizarse completamente antes de la ejecución y podemos nombrar esta "compilación" b) Typecript es un lenguaje de compilación.

Para ayudar a tu imaginación:

function partial func(arguments) {
  return function() {
    return codegenerated(arguments);
  };
}

function partial func(arguments) {
   function codegenerated(arguments){
      // ...
   }
}

Tampoco utilizo clases parciales a menudo: para usarlas de manera efectiva necesitamos almacenar los parámetros de generación de código en la parte "manual" y para leer esta configuración debemos tener un buen analizador de lenguaje (no reflexión). Pero así es: las clases parciales son solo un instrumento de muchos para hacer que la generación de código sea cómoda.

@rpokrovskij
Pido disculpas si me mostré antagonista. No quise ofender ni faltarle el respeto.

Su ejemplo tiene aspectos que requerirían una descripción bastante formal por derecho propio. Originalmente, esta propuesta no se refería a habilitar directamente escenarios de generación de código.

@aluanhaddad ....
"Simplemente evite las clases y su problema se resolverá" - en serio, ¿eso es una solución a un problema? Probablemente si evito usar JavaScript, puedo resolver el problema de toda esta discusión. También es una solución, ¿no? Desafortunadamente, tenemos este lenguaje de mierda en nuestro universo porque tuvo la suerte de entrar en la web hace muchos años. De todos modos, esto es un tema diferente.

"romper casi todas las herramientas conocidas por el hombre" - ¿cómo es eso posible? ¡¿¡¿Cómo?!?! Intento imaginarme rompiendo algo usando clases parciales en TypeScript, pero no puedo. Simplemente no puedo. Por favor, ayúdame aquí con un ejemplo de la vida real.

Ahora imagina que has construido un coche para transportar cajas. Funciona sin problemas. Contratas a un trabajador, que carga el coche. El trabajador toma dos cajas a la vez y las mete en el automóvil. Aún así, todo funciona bien.
Un día el trabajador se marcha a otra empresa y tú contratas una nueva. El nuevo toma una sola caja, la carga, toma otra caja y la carga también. Lógicamente: todo está bien. Pero corres y gritas: detente, detente, ¡tenemos una falla en el sistema! :)

"Simplemente evite las clases y su problema se resolverá" - en serio, ¿eso es una solución a un problema? Probablemente si evito usar JavaScript, puedo resolver el problema de toda esta discusión. También es una solución, ¿no? Desafortunadamente, tenemos este lenguaje de mierda en nuestro universo porque tuvo la suerte de entrar en la web hace muchos años. De todos modos, esto es un tema diferente.

ES2015 tiene muchas cosas incorrectas con seguridad. ES5.1 tenía muchas cosas malas con seguridad. En mi opinión, la falta de clases no era una de esas cosas. Mi punto es que al usar técnicas como el patrón de módulo revelador, se evita la necesidad de clases parciales. TypeScript proporciona azúcar para el patrón de módulo revelador, espacios de nombres.

"romper casi todas las herramientas conocidas por el hombre" - ¿cómo es eso posible? ¡¿¡¿Cómo?!?! Intento imaginarme rompiendo algo usando clases parciales en TypeScript, pero no puedo. Simplemente no puedo. Por favor, ayúdame aquí con un ejemplo de la vida real.

Si usa módulos ES, esto es problemático. Cualquier herramienta que se base en los artefactos físicos del compilador que tienen una correspondencia con sus contrapartes no compiladas estaría sujeta a esto. Dicho esto, TypeScript podría ajustarse a esto en la emisión, un cambio masivo, pero luego emitiría JavaScript que no coincidía con el TypeScript que escribiste.

Entonces, básicamente, la evolución de un lenguaje está siendo frenada por las herramientas, que nunca evolucionarán sin la evolución del lenguaje.
Lo siento, pero veo aquí solo la búsqueda de la razón para no evolucionar.

¿Cuáles son los beneficios que dan las clases parciales, que ya no puedes hacer con el idioma de otra manera? Es un buen azúcar sintáctico, claro, pero ¿qué es lo que realmente gana? ¿Hay algún ejemplo concreto que no sea para hacerlo más parecido a C #?


Aparte: para que conste, comencé como desarrollador de C #, y desde allí pasé a JavaScript / C #. Una vez que se anunció TypeScript 0.8, me vendieron, no porque fuera C # para JavaScript, sino porque tomaba JavaScript estricto y lo hacía mejor para los desarrolladores que disfrutan de los tipos fuertes de C #.

TypeScript no es C # y no debería heredar estrictamente características de C # solo porque C # las tiene. Al igual que C # no debería heredar características de TypeScript. No puedo contar las veces que he querido características como tipos de unión en C # porque son increíbles para TypeScript, pero todavía no son adecuadas para el lenguaje.


Hasta ahora he leído Generación de código y Organización de código.

Generación de código, mire el Aumento de módulos, hay algunas deficiencias que mencioné aquí, pero estoy seguro de que se pueden solucionar.

Organización de código, supongo que usar regiones (risas). Hay muchas otras formas de organizar su código, si tiene una clase de Dios (por el motivo que sea), siempre puede dividirla en muchas clases de Dios. En la superficie, la clase de dios es la importación que todos ven y usan, pero bajo las sábanas, sin embargo, está compuesta de muchas clases, tal vez como propiedades de la clase de dios. Si está utilizando clases parciales para la organización, diría que necesita refactorizar un poco su código para probar y SOLID ify su código.

He podido ajustar el aumento en la mayoría de los escenarios de generación de código que he encontrado hasta la fecha.

Para Omnisharp-Client, uso la extensión de interfaz aquí donde OmniSharp.Api.V2 se genera a través de la reflexión en C #. En la parte inferior del archivo, tengo una herramienta que elimina todos los métodos para esa interfaz.

Para RxJS usamos para mapear todos nuestros métodos que están en Observable .

Simplemente no puedo razonar sobre algunos de los problemas con las clases parciales. Por ejemplo, si nos dan 3 clases parciales.

  • MyAwesomeClass1.ts
  • MyAwesomeClass2.ts
  • MyAwesomeClass3.ts

Alcance: Módulo global (usando namespace / module )

La compilación global es donde esto parece ser más fácil. Como TypeScript ya fusiona los espacios de nombres.

Si mi tsconfig.json solo contiene las rutas de archivo para Class1 y Class2, entonces cualquier cosa que esté en Class3 no se fusiona automáticamente.

  • Al principio, esto parece un error del usuario, pero no será evidente de inmediato por qué no se incluyen los métodos de Class3. Este es un gran problema de usabilidad que generará confusión.

Alcance: Módulos externos (importación / exportación)

Con la compilación externa, cada archivo se considera realmente un módulo (o un ensamblaje infernal en el mundo .NET) porque cada módulo tiene una lista explícita de todas sus dependencias (importaciones) y todas sus api públicas (exportaciones).

¿Cómo razonas sobre estos escenarios?

Dada solo una referencia de archivo, ¿cuál es el comportamiento de la clase?
¿Fusiona automáticamente Class2 y Class3?

  • ¿La definición predeterminada de Class1, Class2 y Class3 exporta exactamente el mismo valor?
  • ¿Qué archivo se emite al disco?
  • ¿Cómo se verá el JavaScript subyacente para todos los formatos compatibles (amd, umd, commonjs, etc.)
// somefile.ts
import { MyAwesomeClass } from 'MyAwesomeClass1';

new mac = new MyAwesomeClass();
mac. // What methods are available here?

¿Tenemos que importar explícitamente todas las instancias de la clase? (Si hacemos eso frustra el propósito en mi opinión).
¿Qué sucede cuando importa todo en un archivo, pero no en otro, a los métodos de transporte?
Esto podría conducir a errores realmente extraños dado un gráfico de dependencia complejo donde la clase se usa una vez, sin los parciales adicionales. ¿Se aumentarían los valores? ¿O los métodos no funcionarían mágicamente hasta que se cargue el otro archivo que los trae?

// somefile.ts
import { MyAwesomeClass } from 'MyAwesomeClass1';
import { MyAwesomeClass } from 'MyAwesomeClass2'; // or maybe import 'MyAwesomeClass2';
import { MyAwesomeClass } from 'MyAwesomeClass3'; // or maybe import 'MyAwesomeClass3';

new mac = new MyAwesomeClass();
mac. // What methods are available here?

@ david-driscoll Solo mis dos centavos aquí:

Es un buen azúcar sintáctico, claro, pero ¿qué es lo que realmente gana?

¿No es el buen azúcar sintáctico el objetivo de cualquier lenguaje? Desde un punto de vista operativo, todo lo que necesitamos son bucles, cláusulas if e iteración para hacer todo. El resto es solo azúcar sintáctico agradable para ser más productivo y proporcionar otras cualidades no funcionales similares.

Y aunque los lenguajes son generalmente buenos en la parte operativa de la instrucción de una computadora, generalmente absorben la parte organizativa de la escritura de código. Tenemos estos artefactos culturales ad-hoc como objetos, clases, herencia y demás, pero no una base teórica sólida, afaik, que realmente explique el dominio de las operaciones de organización y todas las abstracciones que estamos tratando de construir y representar en nuestro código fuente en orden. para poder trabajar con él de una buena manera.

Hay dos cosas que me encantan de TypeScript: 1. Nos permite usar javascript escrito para que de repente sea manejable construir sistemas complejos en el cliente. 2. El sistema de tipos es extremadamente expresivo (porque se vio obligado a poder expresar todo tipo de patrones en javascript que la gente podría escribir sin la limitación de un sistema de tipos).

Pero en lo que respecta a la estructuración del código, no creo que sea mucho menos desagradable que cualquier otro lenguaje. Y desearía que pudiéramos ser más innovadores aquí. Brinde a los usuarios más libertad para experimentar con la parte organizativa del lenguaje. Abrirse y luchar por una nueva expresividad estructural en lugar de ser restrictivo y sospechoso.

Las clases parciales no solo son muy convenientes para la generación de código específicamente, sino que aumentan la libertad en la organización en general. La libertad que necesitamos para salir de este pozo de alquitrán de la organización del código.

@ david-driscoll ¿cuál es el punto de tener parciales? Bueno, una vez más: generación de código.
¿Qué hay de malo en la solución propuesta? Es una solución. Otra solución más (y una solución bastante sucia). Ya tenemos un lenguaje de mierda (javascrtipt), que está lleno de soluciones. En lugar de pensar en sus tareas principales, los desarrolladores de JavaScript pasan mucho tiempo pensando en las soluciones. Con su filosofía propuesta, podemos deshacernos de todo el lenguaje TypeScript. Porque (sorpresa, sorpresa) todo el sistema de tipos se puede simular en javascript puro, utilizando soluciones alternativas. ¿Por qué preocuparse por un lenguaje más expresivo (como Typecript) si puede simular cualquier cosa con soluciones alternativas? ¿Por qué escribir una sola línea de código cuando lo mismo se puede escribir en veinte líneas? ¡Recupere los salarios basados ​​en una serie de líneas de código escritas! El código corto y expresivo es para coños, ¿no es así?

Y no: la solicitud para tener parciales en TypeScript no se basa en la idea de que C # los tenga. No creo que TypeScript deba tener algo por el simple hecho de tenerlo porque otro lenguaje lo tiene.

Y finalmente: ¿por qué crees que si no usas algo, nadie lo necesita?

@greendimka No estaba tratando de provocar ninguna hostilidad: escudo :

Ahora, tenga en cuenta que no estoy en el equipo de TypeScript, solo soy un defensor, pero el equipo ha estado bastante silencioso aquí. Es muy posible que el equipo intervenga cuando tenga algo específico que decir, y lo dejaré si así lo desean.

Uno de los inquilinos clave a los que se ha aferrado el equipo de TypeScript es tratar de mantener TypeScript en sincronía con la dirección de ECMAScript, y evitar hacer características específicas que puedan hacer que se desvíen de la dirección general a la que está evolucionando JavaScript.

No es que no lo usaría (probablemente lo haría si existiera). El problema es que, tal como está en este tema, las clases parciales pueden tener advertencias, por lo que parece dudoso que se implemente en el corto plazo. Probablemente permanecerá así por un tiempo hasta que a) se convierta en una característica imprescindible para una parte interesada de alto valor ob) una propuesta llegue a las últimas etapas del TC39.

No veo el aumento de módulos como una solución alternativa. Le permite aumentar el sistema de tipos y permite muchos de los escenarios de generación de código que se han detallado en este hilo. Este es un paso hacia otras características del idioma.

  • Es feo? Absolutamente, feo como el infierno. Dicho esto, si está generando el código, ¿cuál es la diferencia? Solo su herramienta de generación debe preocuparse por cómo aumentar la clase. Una vez que haya ejecutado su herramienta de generación de código e incluya sus módulos, obtendrá errores completos de intellisense y de compilación, etc.

  • Es perfecto ¡Lejos de ahi! Veo un escenario mucho más común con mixins que necesitan ser compatibles a nivel de idioma. Y creo que algo en línea con los decoradores como se propone aquí tiene sentido, aunque nuevamente no hay ninguna propuesta específica.

@ david-driscoll, lo siento por ser extremadamente grosero. Realmente tiendo a estar del lado del progreso positivo.

Me gustaría esto para poder poner implementaciones estáticas en una clase parcial.

Dado que no parece que esto se materialice pronto, me gustaría proponer una solución alternativa que encontramos. Hay algunos comentarios como este, pero no vi ninguno con estos detalles específicos.

Nos gustaría que los parciales se utilicen en un escenario de generación de código en el que queremos poder extender nuestro código TypeScript generado. Sin embargo, estamos generando modelos que tienen interdependencias, por lo que heredar de nuestras clases no funciona bien en el sentido tradicional. Las clases generadas que hacen referencia a otras clases no terminan con el tipo correcto de objeto al que se hace referencia y terminaríamos con un patrón de fábrica complejo para crear los tipos de instancia correctos.

Terminamos cambiando nuestras clases generadas a clases base, pero solo en la declaración de clase. Luego creamos clases stub que heredamos de nuestras nuevas clases base. Entonces podríamos extender estas nuevas clases y heredarían todo su contenido de la clase base.

Por ejemplo:
Si generamos una clase Person, ahora generaríamos una PersonBase. Todo el código generado va en esta clase. También generaríamos una clase Person vacía que amplíe PersonBase. Esta clase solo se generaría una vez. Luego colocamos todo nuestro código generado en PersonBase, y todo el código personalizado ingresa manualmente en Person. Todas las referencias generadas a Person permanecen como Person y no como PersonBase. De esta manera todo IntelliSense continúa funcionando.

Archivo 1: Clase base

module ViewModels {
    export class PersonBase {
        // Generated members
        public anotherPerson: Person;
        constructor(){
            // Generated constructor code
        }
    }
}

Archivo 2: Clase real

module ViewModels {
    export class Person extends PersonBase {
        // Custom methods
        public NewVariable: string = "NewVar";
        constructor() {
            super();
            // Custom constructor code
        }
    }
}

Esto ha resuelto nuestro problema de clase parcial con el código generado. Felicitaciones a Andrew Scott por tener la idea.

También me gustaría mucho esto, ya que estoy generando un código con un generador de código y necesito agregarle algunos cambios. Las clases parciales permitirán regenerar el código sin tener que volver a agregar los cambios más tarde (lo que puede ser grande o complejo)

Hay una razón importante para tener clases parciales que no creo que se hayan mencionado todavía: migrar gradualmente JS a TS. En Javascript, particularmente antes de ES6, las clases pueden verse como un montón de:

Foo.prototype.someFunction = function() {/*....*/}

Y estos se pueden distribuir en varios archivos sin ningún manejo especial. Convertir una base de código como esa en TypeScript moderno actualmente requiere funciones de movimiento para que todos los miembros de una clase estén en el mismo archivo, lo que puede ser un cambio bastante invasivo. Con clases parciales, la migración se puede realizar utilizando solo ediciones locales.

@RyanCavanaugh Mencionas que todo lo que se pide aquí se puede manejar sintácticamente con otra sintaxis de TypeScript:

Creo que la propuesta de mixin tiene muchas más preguntas abiertas. La propuesta de clase parcial aquí es solo una codificación de cosas que ya están permitidas en TypeScript, solo con algo de azúcar sintáctico (esta característica podría literalmente hacerse como una reasignación sintáctica a cosas que ya tenemos).

Entonces, ¿cómo haría uno para hacer esto en TypeScript ?:
Archivo1.ts:

// Imagine this file is code generated and could be regenerated during development
export partial class Contact
{
    firstName: string;
    lastName: string;
    partial OnInit( args: any ) : void;
    constuctor( args: any )
    {
        this.OnInit( args );
    }
}
export class Address
{
    addr: string;
    city: string;
    state: string;
    zip: string;
}

Archivo2.ts

// See where I'm going with this? This file would be hand edited and allows me to specify associations and children.
partial class Contact
{
    Addresses: string[] = [];
    partial OnInit( args: any ) void
    {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
        this.Addresses.push( new Address() );
    }
}

Transpilar lo anterior emitiría una clase JS para Contact, algo como esto:

var Contact = (function () {
    function Contact() {
         this.Addresses = [];
   }
   Contact.prototype.constuctor = function (args) {
       this.OnInit( args );
   };
   Contact.prototype.OnInit = function (args) {
       this.firstName = args.firstName;
       this.lastName = args.lastName;
       this.Addresses.push(new Address());
   };
   return Contact;
})();

var Address = (function () {
    function Address() {
    }
    return Address;
})();

Tenga en cuenta que al final quiero una clase llamada Contacto y no quiero tener que subclasificar dos clases separadas en una tercera.

@cosmoKenney

Aquí tienes un ejemplo de algo que funcionará. No está tan sintácticamente surgado como partial sin embargo, funciona y es una solución muy funcional.

// address.ts
export class Address
{
    addr: string;
    city: string;
    state: string;
    zip: string;
}
// contact.impl.ts
import { Address } from './address';

// Class implementation, do the things here that are not code generated
export class Contact {
    firstName: string;
    lastName: string;
    addresses: Address[];
    constructor(args: any) {
        this.onInit(args);
    }
}
// extending the interface here
// define methods you know will need in the constructor only
// This only applies to the constructor however
export interface Contact {
    onInit(args: any): void;
}
// contact.partial.ts
import { Contact } from './contact.impl';

// Implement the extended contract here
Contact.prototype.onInit = function(this: Contact, args: any) {
    this.addresses = args.addresses.concat();
    // do stuff;
};

// Adding another method (not part of the extended interface)
Contact.prototype.somethingAwesome = function(this: Contact) {
    // do awesome stuff here
};

// Tell TypeScript "I added this here!!!!"
declare module './contact.impl' {
    interface Contact {
        somethingAwesome();
    }
}
// contact.ts
import { Contact } from './contact.impl';
import './contact.partial';

// Bring it all together (perhaps there are more than one partial class?)
export { Contact };
// main.ts
import { Contact } from './contact';

// use the contact!
const contact = new Contact(['my', 'args']);
const {firstName, lastName} = contact;
contact.somethingAwesome();

Alternativamente, también puede generar código para generar la interfaz y usar la extensión de la interfaz para implementarla.

También puede haber formas de usar la nueva funcionalidad Mixin, sin embargo, todavía no me he sumergido en eso lo suficiente como para dar una respuesta calificada.

@ david-driscoll Vaya. Mi cabeza acaba de explotar.

Me pregunto si la comunidad terminará con una bifurcación, que simplemente admite clases parciales sin resolver un problema.
Hasta ahora no he escuchado ningún argumento real en contra de las clases parciales. Solo unas pocas razones para justificar una pereza.

@greendimka

Me pregunto si la comunidad terminará con una bifurcación, que simplemente admite clases parciales sin resolver un problema.
Hasta ahora no he escuchado ningún argumento real en contra de las clases parciales. Solo unas pocas razones para justificar una pereza.

No estoy seguro de lo que intenta decir aquí. Pero si el sentimiento general dentro de la comunidad es que podemos lograr los mismos resultados utilizando las características del lenguaje existentes, entonces solo necesito algunos ejemplos de cómo hacerlo. Lo que @ david-driscoll publicó arriba está más allá de mi comprensión. Y no parece resolver completamente el problema. De hecho, por lo que puedo decir, malinterpretó mis intenciones.
Mi principal preocupación es generar código para las propiedades escalares de la clase, como un pojo o una clase modelo. Pero necesito poder, en un archivo separado, o incluso en el mismo archivo, pero no dentro de la definición de clase primaria, agregar las propiedades de asociación (para poder pegar una definición primaria recién generada por código sobre la original).
Por ejemplo, mi clase de contacto de arriba obtendría código generado con firstName y lastName. Pero, en mi definición separada es donde agregaría la definición y la inicialización de la colección de objetos Address.
Mi generador de código solo conoce los atributos de la clase, no las asociaciones, por lo que no puede codificar la colección de direcciones, como se hizo en el ejemplo de David.

@cosmoKenney no estaba del todo claro dónde debería existir la dirección. Perdóname por equivocarme en el contexto. Solo intento ayudar a dar las alternativas. Hay muchas funciones que permiten un comportamiento similar sin poner algo en el compilador como clases parciales, que tienen muchas consideraciones adicionales y problemas potenciales para implementar ambos sistemas de módulos (interno / externo).

En su caso, la generación de una clase base abstracta, o la generación de interfaz funcionaría, entonces su otra clase hereda de eso. Entonces, el hecho de que se generó su clase todavía está oculto.

es decir:

// address.partial.ts
export interface IAddress
{
    addr: string;
    city: string;
    state: string;
    zip: string;
}

// address.ts
import { IAddress } from './address.partial';

export class Address
{
    constructor(args: any) {

    }
}
export interface Address extends IAddress{}

// contact.partial.ts
import { IAddress } from './address.partial';

export interface IContact {
    firstName: string;
    lastName: string;
    addresses: IAddress[];
}

// contact.ts
import { Address } from './address';
import { IContact } from './contact.partial';

// Class implementation, do the things here that are not code generated
export class Contact {
    addresses: Address[] = [];

    constructor(args: any) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
        this.addresses.push( new Address("address?") );
    }

    public somethingAwesome() {
        //
    }
}
export interface Contact extends IContact {}

// main.ts
import { Contact } from './contact';

const contact = new Contact(['my', 'args']);
const {firstName, lastName} = contact;
contact.somethingAwesome();

@cosmoKenney : @ david-driscoll ofrece una solución interesante, pero aún carece de la facilidad de las clases parciales reales. Como han demostrado mis experimentos personales, los desarrolladores simplemente no usan "parciales" que se hacen de esta manera. Si bien es una buena solución alternativa, la facilidad y comodidad de los parciales no existe.

Por el momento no sé cómo funciona internamente el compilador de TypeScript. Solo puedo asumir que lee todos los archivos de origen, los procesa y genera archivos JS. Si es así, no veo problemas al leer una sola clase de varios archivos fuente. Si dos o más archivos fuente de una sola clase proporcionan código conflictivo (por ejemplo, la misma propiedad definida dos veces), el compilador simplemente lanza una excepción. Al igual que lo hace si escribe código sintácticamente inválido.

@greendimka , @ david-driscoll
Intenté compilar el ejemplo de David desde arriba, y a ts no le gusta:

C:\TestProjects\TypeScriptFakePartialClassTest>tsc -m amd address.partial.ts address.ts contact.partial.ts contact.ts main.ts
address.ts(4,14): error TS2300: Duplicate identifier 'Address'.
address.ts(10,18): error TS2300: Duplicate identifier 'Address'.
contact.ts(7,14): error TS2300: Duplicate identifier 'Contact'.
contact.ts(11,14): error TS2339: Property 'firstName' does not exist on type 'Contact'.
contact.ts(12,14): error TS2339: Property 'lastName' does not exist on type 'Contact'.
contact.ts(20,18): error TS2300: Duplicate identifier 'Contact'.
main.ts(5,8): error TS2459: Type 'Contact' has no property 'firstName' and no string index signature.
main.ts(5,19): error TS2459: Type 'Contact' has no property 'lastName' and no string index signature.

Además, estoy de acuerdo con @greendimka en que esto es demasiado complejo para usarlo cientos de veces en una aplicación grande.

@greendimka No sé cómo funciona el compilador también, pero no subestimes la tarea. JavaScript no es .NET y los módulos no están disponibles globalmente (literalmente, debería importarlos)

Me encantaría ver esto implementado, pero esta tarea trae varias cosas que deben pensarse cuidadosamente, como:

  • Si define parte de la clase en un archivo A, parte en el archivo B, ¿cuál debería contener la clase completa después de la transpilación? ¿Debería generarse uno nuevo?
  • ¿Todos los archivos que contienen material parcial deben exportar la misma clase?
  • ¿Cómo evitar dependencias cíclicas si necesito el archivo principal en esos adicionales?
  • ¿Qué pasa si exporto más cosas en el módulo que solo la clase en sí?
  • ¿Comprobación de conflictos de nombres?

etcétera...

@greendimka Vea mis respuestas a continuación: (EDITAR: esto fue dirigido a @WoLfulus , lo siento @greendimka):

Si define parte de la clase en un archivo A, parte en el archivo B, ¿cuál debería contener la clase completa después de la transpilación? ¿Debería generarse uno nuevo?

Me encantaría tener el archivo A.js con la definición completa y el archivo B eliminado. Quizás con un comentario enlatado. La mayoría de nosotros estamos empaquetando de todos modos, por lo que la miríada de archivos .js que se empaquetan se convierte esencialmente en un pensamiento posterior al final.

¿Todos los archivos que contienen material parcial deben exportar la misma clase?

Esto se responde prácticamente con mi respuesta a la primera pregunta.

¿Cómo evitar dependencias cíclicas si necesito el archivo principal en esos adicionales?

No sé. ¿No es el trabajo del cargador de módulos reconocer este escenario?

¿Qué pasa si exporto más cosas en el módulo que solo la clase en sí?

No veo el problema con esto. En otras palabras, si B.ts tiene una definición parcial y algunas otras clases no parciales, entonces B.js debe contener el comentario enlatado como se mencionó anteriormente, Y las definiciones de las clases no parciales. Simple, creo.

¿Comprobación de conflictos de nombres?

¿Cómo terminarías con un conflicto de nombres? Si te refieres a la clase, entonces la palabra clave "parcial" resolvería eso. Pero luego, si otros dos archivos definieron clases con el mismo nombre, pero sin la palabra clave "parcial", el compilador debería emitir un error sobre tipos duplicados, como lo hace ahora. Pero si está hablando de dos definiciones parciales de la misma clase que definen una propiedad o método con el mismo nombre, el compilador debería emitir un error sobre miembros duplicados, como lo hace ahora.

@WoLfulus , @cosmoKenney ha respondido bien. Solo para mejorarlo con su primera pregunta (sobre archivos A y B): como diseñador de lenguaje, uno tiene el privilegio de definir reglas. Con eso quiero decir que el archivo A puede tener dicho código "parcial clase X" y el archivo B puede tener dicho código "parcial (archivoX) clase X", que producirá fileX.js.

@cosmoKenney

¿Cómo evitar dependencias cíclicas si necesito el archivo principal en esos adicionales?
No sé. ¿No es el trabajo del cargador de módulos reconocer este escenario?

TypeScript no tiene un cargador de módulos.

@greendimka

Me pregunto si la comunidad terminará con una bifurcación, que simplemente admite clases parciales sin resolver un problema.
Hasta ahora no he escuchado ningún argumento real en contra de las clases parciales. Solo unas pocas razones para justificar una pereza.

No es una buena idea. Por ejemplo, ¿ha considerado cómo mantendrá su bifurcación cuando se agreguen importaciones dinámicas?
Además, se han hecho muchos argumentos en contra de las clases parciales. Romper la correspondencia fuente a fuente entre los archivos TypeScript y JavaScript va en contra de un principio clave del lenguaje. Imagino que una característica necesitaría habilitar un escenario infinitamente más valioso que las clases parciales para que eso siquiera se considere. Probablemente aún sería rechazado.

@WoLfulus , @cosmoKenney ha respondido bien. Solo para mejorarlo con su primera pregunta (sobre archivos A y B): como diseñador de lenguaje, uno tiene el privilegio de definir reglas. Con eso quiero decir que el archivo A puede tener dicho código "parcial clase X" y el archivo B puede tener dicho código "parcial (archivoX) clase X", que producirá fileX.js.

No creo que esté transmitiendo su mensaje. Eso hace que parezca que siente que cualquier limitación que encuentre al expresarse en cualquier idioma existe por razones completamente arbitrarias.
Además, las preguntas de @WoLfulus no fueron respondidas, al menos no para mi satisfacción.

se pone el traje de fuego

TL; DR: Esto no está sucediendo. Si está aquí en el comentario número 189 y desea registrar su desaprobación con eso, lea primero el hilo completo, porque las muy buenas razones para no agregar esto se han cubierto ampliamente anteriormente.

Nuestra interpretación de esta situación es la siguiente:

  • TypeScript ya tiene demasiadas características de clase específicas de TS (declaraciones de propiedades de constructor, inicializadores de miembros, decoradores no finalizados, modificadores de acceso, implements , etc.) para algo que en realidad estaba destinado a ser "Tipos ES6 +" . Parte de eso es culpa nuestra (nuestra dirección de diseño original fue generalmente demasiado OOP) y parte de eso es culpa de ES6 (la propuesta de clase se movió mucho hace cinco años y terminamos teniendo características que no lo lograron). Agregar otra característica de clase específica de TS es otra gota en la espalda del camello que debemos evitar si podemos.
  • Sin ambigüedades, el futuro de JavaScript son los módulos. Las clases parciales solo tienen sentido y funcionan bien en el modelo de "sopa global". Permitir clases parciales que abarquen varios módulos es una receta para el desastre, ya que el orden de carga no es predecible. Agregar una función que solo funciona bien en un universo solo global no es un buen uso del tiempo.
  • C # lo tiene, pero no estamos aquí para reinventar C #. ¡Seriamente!
  • El escenario del "código generado" no es convincente por sus méritos. Cualquier emisión plausible para clases parciales restringiría los inicializadores de propiedad y el código del constructor a exactamente una declaración, y esto sería un factor decisivo para las configuraciones de estilo .Designer.cs ya que tanto el código de usuario como el código generado probablemente necesitarían inicializadores o lógica de constructor .
  • Hay muchos, muchos otros mecanismos de composición disponibles en JS: mixins, Object.assign , etc. El escenario que llevó a esta característica en C # fue impulsado por restricciones que simplemente no están presentes en JS.
  • La solución actual de interface + prototype.method = ... no permiten el escenario de código generado igual de bien que partial class haría. Es extraño escribir ese código a mano, pero no hay problema para generar código de manera diferente, por lo que si realmente cree que una configuración de código generado por archivo dividido es la única forma de resolver su problema (soy escéptico), entonces ese método está disponible para usted .
  • Consideramos que ES6 es el árbitro de "debe tener" para las clases. No hay nada especial en TypeScript (aparte de su atractivo para los programadores de C #) que haga que necesite clases parciales más de lo que lo haría JavaScript sin tipar. Entonces, si hay algún escenario que realmente lo deja fuera del parque para agregar clases parciales, entonces ese escenario debería poder justificarse a través del proceso TC39. Si eso gana tracción, estaremos encantados de opinar sobre ello, pero esto ni siquiera parece estar en el radar de nadie.

@RyanCavanaugh sobre esto:

La solución alternativa actual de interface + prototype.method = ... habilita el escenario de código generado tan bien como lo haría una clase parcial.

Como mencioné más de una vez, estoy de acuerdo con que haya una forma no parcial, no C # de lograr lo que necesito, y solo pido un ejemplo de trabajo. david-driscoll fue muy útil, pero publicó un código que no se compila. Entonces, si cree que puedo generar código para generar una clase con propiedades escalares solo para, por ejemplo, el nombre, el apellido, como el anterior, está bien. Solo necesito una forma de agregar una colección de otros tipos como una propiedad en la clase codificándola manualmente. Pero también necesito una forma de manejar la inicialización de ese conjunto.

@cosmoKenney ¿en su sitio copió el código correcto? El segundo ejemplo tenía interfaces en lugar del mismo nombre de clase. Puedo adjuntar un zip del código si lo desea. Solo dudaba porque los archivos aleatorios en Internet podrían ser malos y no quería que pensaras que estaba publicando algo malicioso 🙂

Lamento escuchar esto, pero respeto las decisiones de diseño que se toman aquí. Tengo un par de preguntas si está bien:

TypeScript ya tiene demasiadas características de clase específicas de TS (declaraciones de propiedades de constructor, inicializadores de miembros, decoradores no finalizados, modificadores de acceso, implementos, etc.) para algo que en realidad estaba destinado a ser "Tipos ES6 +". Parte de eso es culpa nuestra (nuestra dirección de diseño original fue generalmente demasiado OOP) y parte de eso es culpa de ES6 (la propuesta de clase se movió mucho hace cinco años y terminamos teniendo características que no lo lograron). Agregar otra característica de clase específica de TS es otra gota en la espalda del camello que debemos evitar si podemos.

Teniendo esto en cuenta, ¿una posible forma de avanzar sería distinguir claramente, desde el punto de vista organizativo y de código, entre la mecanografía pura de JS y las otras construcciones de lenguaje que ofrece TS? Por mi parte, me encantan las funciones de mecanografía y el lenguaje adicional. Como consumidor / desarrollador, no me importa mucho el hecho de que TS agregue cosas que no están en JS. Solo dame cosas increíbles, si TC39 no las quiere, no me importa.

Si TS encapsulara estas cosas entre sí, ¿quizás eso sería más fácil de reutilizar y ser bifurcado y construido por proyectos como SoundScript, en lugar de reemplazarlo?

Y luego, otro proyecto podría basarse en esta escritura para proporcionar mejores / otras construcciones de lenguaje que las que ofrece JS.

Sin ambigüedades, el futuro de JavaScript son los módulos. Las clases parciales solo tienen sentido y funcionan bien en el modelo de "sopa global". Permitir clases parciales que abarquen varios módulos es una receta para el desastre, ya que el orden de carga no es predecible. Agregar una función que solo funciona bien en un universo solo global no es un buen uso del tiempo.

¿Es realmente? Seguro que los módulos serán una parte natural de JavaScript, pero ¿qué pasa con el cuerpo de código original principal? Me encantan los namspaces y no contiene sopa si se hace correctamente. Lo estoy compilando todo en un archivo y no necesito cargadores de módulos y, en casi todos los casos, no necesito carga de módulos asincrónica. Algunas cosas propuestas como https://github.com/Microsoft/TypeScript/issues/420 solo serían posibles y relevantes con espacios de nombres. ¿Estás diciendo que cosas como esta no van a suceder porque solo deberíamos usar módulos? ¿Están los espacios de nombres camino de la desaprobación?

@ david-driscoll Gracias, ya has sido lo suficientemente útil. Dado que @RyanCavanaugh es quien sigue cerrando el problema y afirmando que hay soluciones, tal vez debería proporcionar un ejemplo.

También veo muchos conceptos erróneos sobre qué son las clases parciales. Es realmente una construcción simple, y todos están exagerando esto:
https://msdn.microsoft.com/en-us/library/wa80x488.aspx

Teniendo esto en cuenta, ¿una posible forma de avanzar sería distinguir claramente, desde el punto de vista organizativo y de código, entre la mecanografía pura de JS y las otras construcciones de lenguaje que ofrece TS? Por mi parte, me encantan las funciones de mecanografía y el lenguaje adicional. Como consumidor / desarrollador, no me importa mucho el hecho de que TS agregue cosas que no están en JS. Solo dame cosas increíbles, si TC39 no las quiere, no me importa.

Eso sería una reescritura de un lenguaje y romper el código de todos.

Además, el "Solo dame cosas increíbles" es fundamentalmente diferente a los objetivos de diseño de TypeScript . Si solo desea cosas increíbles y no le importa la alineación con JavaScript, debe elegir un lenguaje diferente, como CoffeeScript , Dart o Haxe . Por supuesto, tal vez haya razones por las que no les está yendo tan bien como TypeScript en la adopción ...

alguien que sigue cerrando el problema y afirma que hay soluciones, tal vez debería proporcionar un ejemplo de solución

Hay muchos casos de uso para lo que resuelve partial . Casi cualquier construcción de lenguaje tiene muchos casos de uso para los que es válido, pero por buenas razones, debe elegir y elegir.

Sigues pidiendo una función de lenguaje específica, como en "Me encantaría un coche ecológico", pero no estás llegando al meollo del problema, ¿por qué el coche debe ser ecológico? Si explicó por qué su automóvil debe ser verde, o si está abierto al hecho de que en realidad no le importa el color de su automóvil, siempre que satisfaga sus necesidades reales.

No veo por qué siente que es obligación de Ryan decirle cómo repintar su automóvil, solo porque por razones válidas la compañía automotriz ha decidido no producir automóviles ecológicos.

(Ah, y las clases mixin agregadas en TypeScript 2.2 son una buena manera de lograr lo que sospecho que es su verdadera necesidad)

interface Base {}

interface Constructor<T> {
    new (...args: any[]): T;
    prototype: T;
}

interface PartialClass {
    foo(): void;
}

function PartialClass<B extends Constructor<Base>>(base: B): B & Constructor<PartialClass> {
    return class extends base {
        foo() {
            console.log('foo');
        }
    };
}

class MyBase {
    bar() {
        console.log('bar');
    }
}

const MyPartialBase = PartialClass(MyBase);

const instance = new MyPartialBase();

instance.bar();
instance.foo();

Los mixins de

  • Los mixins son unidades autónomas que se pueden agregar a clases existentes.
  • Las clases parciales pueden acceder a miembros desde cualquier otro parcial, como si fueran parte de la misma clase.

Como ejercicio, intente implementar el siguiente código dividido con mixins:

partial class Point {
  readonly x: number;
  readonly y: number;
}

partial class Point {
  translate(dx: number, dy: number): Point {
    return new Point(this.x + dx, this.y + dy);
  }
}

Como ejercicio, intente implementar el siguiente código dividido con mixins

Entonces, volviendo al punto ... Entonces, en lugar de señalar una característica del lenguaje, ¿qué problema está tratando de abordar con su ejemplo? Porque la solución alternativa en TypeScript es fácil:

class Point {
  readonly x: number;
  readonly y: number;
  translate(dx: number, dy: number): Point {
    return new Point(this.x + dx, this.y + dy);
  }
}

(y si somos completistas, pero de nuevo, resuelve un uso, no todos los casos de uso)

interface PointBase {
    x: number;
    y: number;
}

interface Constructor<T> {
    new (...args: any[]): T;
    prototype: T;
}

interface TranslatePointPartial {
    translate(dx: number, dy: number): TranslatePointPartial;
}

function TranslatePointPartial<B extends Constructor<PointBase>>(base: B): B & Constructor<TranslatePointPartial> {
    return class TranslatePointPartial extends base {
        translate(dx: number, dy: number): TranslatePointPartial & PointBase {
            return new TranslatePointPartial(this.x + dx, this.y + dy);
        }
    };
}

class Point {
    readonly x: number;
    readonly y: number;
}

const MyPoint = TranslatePointPartial(Point);

const instance = new MyPoint();

instance.x;
instance.y;
instance.translate(1, 2);

@ Elefante-Vasija

Teniendo esto en cuenta, ¿una posible forma de avanzar sería distinguir claramente, desde el punto de vista organizativo y de código, entre la mecanografía pura de JS y las otras construcciones de lenguaje que ofrece TS?

No veo una manera de desenredar los dos en este momento. En teoría, se podría escribir algún lenguaje en CoffeeScript que se transforme sintácticamente a TypeScript y usar ese lenguaje de nivel superior para agregar nuevas características sintácticas mientras se sigue usando el sistema de tipos de TS, pero eso solo te llevará hasta cierto punto. Simplemente elegir uno de los muchos otros lenguajes de compilación en JS totalmente nuevos parece una mejor opción si ese es el objetivo.

¿Estás diciendo que cosas como esta no van a suceder porque solo deberíamos usar módulos? ¿Están los espacios de nombres camino de la desaprobación?

Los espacios de nombres no van a ninguna parte, seguro. Son solo un nombre de un patrón que ya está en uso generalizado y todavía tienen mucho sentido en un mundo de módulos ES6. Los módulos son cada vez más la forma en que las personas organizan su código y tienen muchos beneficios secundarios excelentes. Solo digo que las clases parciales casi no tienen sentido en ese mundo, y sería extraño agregar un patrón organizativo fundamentalmente nuevo que es incompatible con él. Si el mundo se moviera en la dirección opuesta y todos dijeran "Los scripts globales con efectos secundarios en carga son increíbles, todos se divierten en los prototype s de los demás", tal vez nos sentiríamos de manera diferente.

@RyanCavanaugh

Quizás sea así ... Quizás no tengan tanto sentido ... Pero como he dicho varias veces, creo que incluso en este hilo, las clases parciales tienen que ver con facilitar la generación de código ... Ese es el principal beneficio que veo de ellos vale la pena la función en TS ... Yo, y tengo que asumir muchos otros, pero eso es solo una suposición, actualmente estoy generando toneladas de código TS desde mi código C # ... Pero luego la pregunta es cómo extender de manera segura el código generado Código TS ??? Esta es una pregunta mucho más fácil de responder con clases parciales ... Actualmente estamos recurriendo a hacks para mantener el código personalizado en nuestros archivos de código TS generados ...

Gracias

Lo mismo aquí, generamos el "proxy" mecanografiado para nuestras API de C #, y nos gustaría poder extender estos objetos en mecanografiado fácilmente.

No soy un especialista en JS, así que debo estar perdiendo algo, porque no veo por qué ser capaz de dividir la declaración de clases en varios archivos TS (con solo una declaración de clase en un solo archivo JS después de la compilación) sería un problema para el mundo JS. Por lo que yo veo, es transparente para JS, como si se hubiera escrito en un archivo desde el principio. Quiero decir, si usa clases parciales o no, la salida JS será exactamente la misma, pero evitará el costo de la extensibilidad del objeto JS para nuestros desarrolladores.

@RyanCavanaugh

Otra cosa que quería mencionar es que yo personalmente no le estoy pidiendo al compilador de TS que resuelva de alguna manera el dilema de empaquetado que podría surgir al crear un módulo ES6 usando clases parciales en diferentes archivos TS ... Tiene mucho sentido para mí al menos para compile varios archivos TS en un solo archivo js para el módulo ES6 ... Una vez más, las clases parciales ayudan significativamente a la generación de código ... Permiten que las herramientas creen grandes partes de su modelo con extensibilidad confiable ...

@kitsonk

Sigues pidiendo una función de lenguaje específica, como en "Me encantaría un coche ecológico", pero no estás llegando al meollo del problema, ¿por qué el coche debe ser ecológico? Si explicó por qué su automóvil debe ser verde, o si está abierto al hecho de que en realidad no le importa el color de su automóvil, siempre que satisfaga sus necesidades reales.

No, ya no pido una función nueva. Se ha dicho una y otra vez que el lenguaje puede lograr lo que quiero. He proporcionado ejemplos de lo que quiero (en C #), pero todavía tengo que ver en los ejemplos útiles una forma de llegar a donde necesito estar.

No veo por qué siente que es obligación de Ryan decirle cómo repintar su automóvil, solo porque por razones válidas la compañía automotriz ha decidido no producir automóviles ecológicos.

Ryan parece ser un moderador aquí y está cerrando el tema. Es por eso. Si él es una autoridad del lenguaje, entonces tal vez no debería cerrar el problema antes de hacer un esfuerzo por comprender mi necesidad y rechazar esta solicitud sin dar un ejemplo de cómo hacerlo con las características del idioma nativo. He dicho que me gustaría una solución en tiempo de compilación. Soy un desarrollador bastante bueno y llevo un tiempo programando en muchos lenguajes diferentes y desconocidos, y todavía me cuesta mucho la sintaxis mixin. No entiendo cómo algo tan complejo y difícil de entender se introdujo en el lenguaje y, sin embargo, todos rechazan la elegancia de la sintaxis de clase parcial, simplemente, parece, porque está tomado de C #.

Ryan parece ser un moderador aquí y está cerrando el tema. Es por eso. Si es una autoridad lingüística, quizás no debería cerrar el asunto antes de hacer un esfuerzo por comprender mi necesidad.

🤔

@RyanCavanaugh , sé que has hecho un esfuerzo por ayudar. Y en este punto paso a probar mixins. O simplemente herencia, o tal vez tengo tanta prisa por terminar este proyecto que estoy pasando por alto una forma de hacer esto con genéricos. Mmm...
Con herencia y naming, puedo hacer:

class AddressBase // this is a code generated class
{
    public address: string;
    public city: string;
    public state: string;
    public zip: string;

    constructor( jsonFromService: any )
    {
        this.OnInit( jsonFromService );
    }

    OnInit( jsonFromService: any )
    {
        // could use Object.assign here
        this.address = jsonFromService.address;
        this.city = jsonFromService.city;
        this.state = jsonFromService.state;
        this.zip = jsonFromService.zip;
    }
}

class ContactBase // this is also a code generated class
{
    public firstName: string;
    public lastName: string;

    constructor( jsonFromService: any )
    {
        this.OnInit( jsonFromService );
    }

    OnInit( jsonFromService: any )
    {
        // could use Object.assign here
        this.firstName = jsonFromService.firstName;
        this.lastName = jsonFromService.lastName;
    }
}

// classes that extend the functionality of the code generated classes:
class Address extends AddressBase // subclass simply because I don't want to have to use AddressBase all over my codebase, and then refactor if I ever extend the class
{
}

class Contact extends ContactBase
{
    public Addresses: Address[] = []; // THIS is the customization/extension that cannot be code generated.

    OnInit( jsonFromService: any )
    {
        // note that jsonFromService receives a dto with a array of address info
        super.OnInit( jsonFromService );

        for ( let addr of jsonFromService.Addresses )
        {
            this.Addresses.push( new Address( addr ) );
        }
    }
}

Y eso funciona bien para mí. No es tan bonito y obliga a mis clases generadas por código a tener nombres que no quiero usar en el código. Pero, de nuevo funciona.

En este momento siento que los tipos, que están fuertemente en contra de las clases parciales, simplemente entienden mal el concepto de clases parciales, y piensan que las clases parciales son algo que las clases parciales no son.

Para dejarlo claro: la clase parcial es una construcción sintáctica, que le permite dividir la definición de una misma clase en varios archivos físicos. Las palabras clave aquí son "iguales" y "simples".

La documentación de TypeScript da una mala definición de clases parciales (primer párrafo aquí: https://www.typescriptlang.org/docs/handbook/mixins.html ).

Ejemplo de concepto de clases parciales reales :

Archivo "Point.generated.ts":

partial class Point {
   readonly x: number;
   readonly y: number;
}

Archivo "Point.codeByHand.ts":

partial class Point {
  constructor(x: number, y: number) {
      this.x = x;
      this.y = y;
  }

  translate(dx: number, dy: number): Point 
  {
      return new Point(this.x + dx, this.y + dy);
  }
}

Se compilan en "Point.js", y el nombre proviene del nombre de una clase, no de los nombres de los archivos:

var Point = (function () {
    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    Point.prototype.translate = function (dx, dy) {
        return new Point(this.x + dx, this.y + dy);
    };
    return Point;
}());

Los mixins, cuando se usan como "clases parciales" (y creo firmemente que este término en la documentación se usa incorrectamente ) en realidad representan una herencia múltiple ( https://en.wikipedia.org/wiki/Multiple_inheritance ).

Entonces, como vemos, discutimos dos herramientas diferentes: clases parciales reales y herencia múltiple. Estas son dos herramientas diferentes. Y como cualquier herramienta, tienen su propia área de aplicación. En otras palabras: un carpintero puede usar un martillo en lugar de un mazo, cuando el mazo no está disponible, pero el martillo no es la herramienta adecuada.

En mi humilde opinión, esta discusión no tiene futuro y deberíamos detenerla. Si bien todavía no veo problemas tecnológicos para las clases parciales

El equipo de TS no quiere implementar características específicas de TS si es posible. Si desea discutir más, este es un lugar mejor: https://esdiscuss.org/topic/class-syntax-enhancements

Espero que haya un repositorio de GitHub en el lado de TC39 para discutir estas cosas en lugar de enviar uno basado en correo: /

@greendimka

En este momento siento que los tipos, que están fuertemente en contra de las clases parciales, simplemente entienden mal el concepto de clases parciales, y piensan que las clases parciales son algo que las clases parciales no son.

Para dejarlo claro: la clase parcial es una construcción sintáctica, que le permite dividir la definición de una misma clase en varios archivos físicos. Las palabras clave aquí son "iguales" y "simples".

No creo que nadie aquí esté malinterpretando la definición de clases parciales, creo que quizás estás malinterpretando la definición de clases en JavaScript.

Si tiene la impresión de que las clases de JavaScript se parecen en algo a las clases que se encuentran en prácticamente cualquier otro lenguaje de uso generalizado, es muy posible que le parezca que otras personas están malinterpretando.

Sin embargo, como se ha dicho varias veces en esta discusión, sé que lo he dicho explícitamente al menos una vez, las clases de JavaScript no son construcciones declarativas, son imperativas.

No existen hasta que se ejecutan las declaraciones.

Definirlos depende de la mutación.

Esto ya los vuelve frágiles y dependientes del orden en lo que respecta a la herencia.

Esto podría seguir y seguir...

@cosmoKenney

No entiendo cómo algo tan complejo y difícil de entender se introdujo en el lenguaje y, sin embargo, todos rechazan la elegancia de la sintaxis de clase parcial, simplemente, parece, porque está tomado de C #.

Realmente no creo que eso sea correcto. Todo el mundo ama C #, incluso los programadores de Java 😛, y TypeScript no es diferente por el simple hecho de ser diferente.

@aluanhaddad

Creo que quizás estás malinterpretando la definición de clases en JavaScript ...

Para citado y más abajo: estoy hablando de TypeScript.
Como dije: no veo ningún problema técnico al implementar clases parciales reales en TypeScript, que se compilarán en construcciones de JavaScript.

@greendimka

Para citado y más abajo: estoy hablando de TypeScript.
Como dije: no veo ningún problema técnico al implementar clases parciales reales en TypeScript, que se compilarán en construcciones de JavaScript.

El problema con esta afirmación es que no existe una clase TypeScript.

¡Oh chico! Algunas personas usarán cualquier cosa como razón.
Ok, no hay clases en TypeScript. O clases de TypeScript. O lo que sea. Lo que.

Quiero decir que son clases de JavaScript. Si bien a veces soy culpable de ser pedante, hago esta distinción muy específicamente porque necesitamos estar de acuerdo en la definición del término clase antes de poder discutir qué características son fáciles de agregar.

Creo que no queremos decir lo mismo cuando hablamos de clases y eso provoca disonancia cognitiva e impide el entendimiento mutuo.

Creo que no queremos decir lo mismo cuando hablamos de clases y eso provoca disonancia cognitiva e impide el entendimiento mutuo.
¡Totalmente de acuerdo!

De hecho, la primera publicación de este tema describe perfectamente lo que se solicitó, por lo que esta discusión no tiene futuro :)

@greendimka sí, no tiene futuro ya que parece que TypeScript nunca tendrá clases parciales. En mi opinión, eso es bueno.

Sin embargo, los malentendidos y la mala comunicación no son cosas buenas, por eso sigo conversando contigo.

Desafortunadamente, no parece estar interesado en ninguno de los dos

R. Explicando qué son las clases de JavaScript, uno podría cambiar de opinión y llegar a estar de acuerdo con usted en que pueden hacerse parcializables de manera trivial.

B. Aprender de los demás sobre qué son las clases de JavaScript para que comprenda el punto de vista opuesto.

Creo que es una pena, pero no lo discutiré más si así lo desea.

No estoy en contra de ninguna discusión. Pero se ha dicho varias veces aquí que las clases parciales no se implementarán (al menos no ahora). Por eso creo que es mejor concentrarse en otras cosas. Quizás TypeScript pueda cambiarse en el futuro, quién sabe.
En relación con A y B. Odio JavaScript. Pero lo conozco muy bien y, por supuesto, sé cómo se "representan" las clases en él. Pero el objetivo de toda esta discusión no era modificar JavaScript, sino mejorar las capacidades de TypeScript, como un lenguaje superior, que produce código JavaScript de "bajo nivel".
Imagine que reemplazaríamos TypeScript con C ++ y JavaScript con código de máquina. El código de máquina carece de la mayoría de los conceptos que existen en C ++. Pero, ¿debería C ++ dejar de evolucionar por eso? Claro que no. ¿Debería dejar de evolucionar porque algunos compiladores antiguos no podrán compilar? Por supuesto que no - les das una nueva característica a los desarrolladores y les dices: funciona con nuevos compiladores - tú (los desarrolladores) decides si quieres usarla.

@aluanhaddad
Hombre, oh hombre. ¿A quién le importa la parte de Javascript de la ecuación? TypeScript es una capa superior, o como quieras expresarlo. ¿Recuerda las viejas discusiones sobre 4GL vs 3GL? TypeScript es para Javascript como un 4GL es para un 3GL, más o menos. Además, el argumento de que TypeScript es ES6 con tipos fuertes, por lo que Partial Classes está fuera del alcance de la hoja de ruta de TypeScript es LAME. Tenemos Mixins, Generics, Modules, Name Spaces y Type Casting. Entonces, ¿por qué no hacer un esfuerzo adicional con las clases parciales?

Todo lo que queremos de las clases parciales es el azúcar sintáctico que nos permite amalgamar todas las diversas definiciones de una sola clase de TypeScript en una definición final de clase Javascript de bajo nivel 3GL. No debería haber ningún impacto en la definición final de la clase de JavaScript, ¿verdad? ¿Por qué el producto final de la transpilación a Javascript forma parte de esta discusión? Seriamente.

@cosmoKenney "es solo ES.next con tipos" es un gran punto de venta que convierte a los desarrolladores de JavaScript. Si quieres algo más, estás buscando en el idioma equivocado. ¿Quizás probar Scala.js en

editar: Acabo de darme cuenta de que en ES.next las clases parciales se pueden implementar con un cargador de módulos personalizado. Si utiliza

import {MyClass} from './myclass.*'

el cargador podría fusionar todas las definiciones de MyClass exportadas de cualquier archivo que coincida con el comodín en una sola clase y luego proporcionar eso.

@spion Me gusta cómo te refieres correctamente a ellos como clases parciales ES.next. Un cargador de módulo ES, como un navegador, o un cargador de polyfill, como SystemJS, admitiría clases parciales.
Uno de los principales problemas con la adición de esta función en el nivel de TypeScript es que rompe las herramientas existentes, como cargadores y empaquetadores. Por otro lado, si ECMAScript los especificara, todas estas herramientas implementarían la función y TypeScript seguiría siendo compatible con todas estas herramientas.

Definitivamente estoy de acuerdo contigo con respecto a Scala.js

@cosmoKenney

Tenemos Mixins, Generics, Modules, Name Spaces y Type Casting. Entonces, ¿por qué no hacer un esfuerzo adicional con las clases parciales?

Los mixins, como se ve en TypeScript, son un patrón de diseño ECMAScript que TypeScript _types_.

Los genéricos son una característica del sistema de tipos, por lo que no se aplican.

Los módulos son una característica de ECMAScript.

Los espacios de nombres son un azúcar sintáctico y una formalización de un patrón de diseño ECMAScript.

La conversión de tipos no existe en TypeScript.

@aluanhaddad, ¿por qué sigues haciendo esto como algo de tiempo de ejecución? Cargadores de módulos y todo lo que no tiene nada que ver con el transporte.

+1

@cosmoKenney

@aluanhaddad, ¿por qué sigues haciendo esto como algo de tiempo de ejecución? Cargadores de módulos y todo lo que no tiene nada que ver con el transporte.

Eso no es correcto.

Mire los ecosistemas SystemJS y Webpack y verá lo contrario.

Los flujos de trabajo aún más tradicionales y sólidos como una roca se basan en la correspondencia entre los archivos de entrada y salida.

+1

Necesito una clase parcial porque estamos generando la mayor parte de la clase base usando herramientas (para automatizar cuando hay cambios en el modelo). Luego usamos la clase parcial para agregar funcionalidad a la clase en archivos separados para que la herramienta no la sobrescriba (que genera automáticamente la clase).

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

dlaberge picture dlaberge  ·  3Comentarios

bgrieder picture bgrieder  ·  3Comentarios

siddjain picture siddjain  ·  3Comentarios

Antony-Jones picture Antony-Jones  ·  3Comentarios

fwanicka picture fwanicka  ·  3Comentarios