Typescript: Aulas parciais

Criado em 29 ago. 2014  ·  224Comentários  ·  Fonte: microsoft/TypeScript

Adicione suporte de classes parciais. Mixins não é o mesmo, porque é uma realização em tempo de execução. Precisa de realização de compilação, onde classes parciais serão combinadas em uma antes de converter o texto digitado em 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!"; }
}

vai ser:

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

Comentários muito úteis

Um caso de uso para classes parciais é para classes de proxy geradas. Por exemplo, invólucros WebAPI ou SignalR. Seria muito bom poder estender as classes de proxy geradas com lógica customizada. Especialmente ao gerar classes para modelos, seria bom poder anexar lógica de negócios diretamente às classes de modelo retornadas da API.

Todos 224 comentários

Acho que você emprega "parcial" nos módulos também e ajuda a resolver esse problema levantado aqui.

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

@disshishkov talvez os métodos de extensão sejam suficientes: # 9

Para que tipo de situação você deseja usar e como as soluções existentes ficam aquém disso?

Observe que isso foi implementado em C # para oferecer suporte a cenários de edição do tipo WinForms onde você tem um arquivo gerado automaticamente e um arquivo editado pelo usuário contribuindo para o mesmo tipo; Não tenho certeza se esse tipo de situação se aplica a JavaScript / TypeScript.

Algumas classes podem ter muitos números de linha, apenas para uma melhor leitura, a divisão em várias classes separadas ajudará. Você pode dividir por tipo diferente, por exemplo, por lógica (no caso em que essa lógica não pode ser movida para outra classe), por visibilidade (privada e pública) e outras. No caso em que a combinação de classes parciais pode ter problemas (por exemplo, 2 classes parciais mesmo o mesmo método / declaração de variável), o compilador deve notificar e lançar um erro.

Isso não é realmente convincente. Se sua classe é tão grande que não pode ser editada confortavelmente em um único arquivo devido ao seu tamanho, é um cheiro de design muito importante (por exemplo, comentários em http://programmers.stackexchange.com/questions/157482). A divisão por tipo / lógica / visibilidade / etc diferente é algo que pode ser tratado por um IDE ou por uma organização em um único arquivo.

Vale a pena discutir por que suas classes são tão grandes que não podem ser navegadas (é porque as ferramentas de navegação deveriam ser melhores?), E se existem maneiras melhores de a linguagem suportar a _decomposição_ de uma classe ao invés de apenas _dividi-la_.

Pessoalmente, acho que "classes parciais" deveriam existir, mas se comportam como módulos que se fundem. Tenho um sistema com módulos que, embora o intellisense veja todos os módulos (como deveria), o JS real que o contém só é carregado quando necessário. Eu acho que o mesmo seria ótimo para as classes também - para carregar apenas as partes necessárias. Eu também queria criar uma função e usar uma classe para expandi-la ainda mais. Atualmente, você só pode fazer isso com módulos.

Um caso de uso para classes parciais é para classes de proxy geradas. Por exemplo, invólucros WebAPI ou SignalR. Seria muito bom poder estender as classes de proxy geradas com lógica customizada. Especialmente ao gerar classes para modelos, seria bom poder anexar lógica de negócios diretamente às classes de modelo retornadas da API.

+1 kvantetore.

O caso de uso é exatamente o mesmo que .net; uma parte da classe é gerada (em nosso caso, a partir de esquemas Avro) e você deseja adicionar código auxiliar adicional para trabalhar com as classes geradas.

Eu gosto dessa sugestão. Gostaria de ter classes parciais para separar atributos / propriedades de classes que formam minha "hierarquia de dados" que poderiam ser reunidos em um único arquivo, de seus métodos que podem ser divididos em vários outros arquivos. Isso tornaria o código mais claro e fácil de entender em um piscar de olhos.

Meu arquivo de hierarquia de dados :

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

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

Arquivo contendo métodos da classe A :

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

Arquivo contendo métodos da classe 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 mim. Eu gostaria de usar modelos tt para gerar classes Typescript, mas adicioná-los em um arquivo separado que não será substituído pela geração de modelo.

+1 Isso vai realmente me ajudar com minhas aulas geradas automaticamente que preciso estender

Partial Class é muito bom, mas não faz sentido tê-la. Mostrou que você tem um problema de design. A regra de ouro na programação é mantê-la simples e estúpida (KISS), independentemente das linguagens que você usa, é separar grandes classes em menores - por 1) dividir os scripts em classes e chamá-los (outras classes também podem chamar isso classes menores também em vez de reinventar a roda), ou 2) separar e converter em Abstrato / Interface / Herança / Virtual etc. ou polimorfismo.

Digamos que você tenha um veículo com tudo dentro dele. Uma classe parcial não faz sentido e apresenta complexidade e sobrecarga aqui, onde faz mais sentido fazer isso. Crie uma classe Engine, classe Tranny, classe Drivetrain, classe Door e classe Tire como classes separadas e mova scripts para elas, que quando chamados ainda podem definir um Vehicle dentro da classe Vehicle. Reduz scripts longos e complexidade de script. Provavelmente, você descobrirá que tem scripts de porta aqui e ali, que podem ser simplificados por uma classe de porta. Interface / Abstract / Inheritance / Virtual pode ser usado para alterar a definição da classe Door em alguns scripts na classe Vehicle.

Também é mais provável que você tenha menos tempo de desenvolvimento dessa maneira. Certa vez, tive o ASP.NET C # Blog que usa muitas classes parciais. Eu tinha lutado com isso porque tinha muitas aulas parciais e você não sabe onde elas estão. É como lidar com a lógica GOTO na programação. Muito ruim !! Eu nunca fui capaz de criar um patch para a correção de bug com sucesso, nem fui capaz de personalizar o script porque ele estava muito enterrado em uma pilha de classes parciais (então faça algumas redações renomeadas).

Apenas dizendo isso como um pensamento de 1 centavo da minha mente.

@ fletchsod-developer: Algumas situações são tratadas adequadamente por herança, mas não todas. E alguns desenvolvedores podem usar indevidamente classes parciais, mas não todas. Existem algumas situações em que considero as classes parciais muito, muito úteis, mas se você não gosta delas, não precisa usá-las.

@ jfrank14 , como sugerido acima por @basarat # 9 não funcionaria para você?

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

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

@NoelAbrahams , você seria capaz de acessar as definições do arquivo 1 no arquivo, mas não vice-versa? Tudo bem para mim, desde que seja independente da ordem de inclusão.

Eu realmente espero que vocês considerem classes parciais como C # para TS ... para mim, a única razão é a geração de código, que é a principal razão por trás das classes parciais de C # ... atualmente estamos fazendo muita geração de código TS, e esperamos fazer mais e mais ... atualmente, temos que contar com "//

+1 - poderia realmente usar isso para adicionar funcionalidade a classes geradas automaticamente; não existe nenhuma outra solução elegante que eu possa pensar ou sobre a qual tenha lido até agora ...

+1 - Classes geradas por código. Objetos C # / Java serializados para o cliente

Precisa disso também para mesclar classes geradas!

Este seria um recurso muito útil para permitir a fusão simples do código gerado com o código não gerado.

+1

+1 - Também precisamos dele para classes parcialmente geradas, seria mais elegante do que a herança que emite um pouco de código que não é necessário para este propósito.

+1

Encontrou esta limitação novamente - por favor, adicione este recurso! : +1:

+1

+1

+1

+1

Pessoas marcam com +1 aleatoriamente isso não vai mudar.

Os casos de uso até agora são:

  • Tenho aulas muito grandes que abrangem vários arquivos. @RyanCavanaugh indicou que provavelmente era um grande problema de design com código tão complexo e insuficiente para garantir a complexidade da implementação.
  • É como eles.
  • Um outro argumento parece ser C # os tem.
  • O outro argumento é em torno do código gerado de outras linguagens.

O segundo e o terceiro caso de uso não são convincentes e o quarto, eu suspeito que precisa de algum nível de explicação de como entregar o nº 9 não atenderia a um requisito muito semelhante e até mesmo aos Decoradores (nº 2249).

Parece que o # 9 realizaria a mesma coisa e seria ainda mais geral (porque você poderia estender outras classes além da sua). Mas pode ser razoável querer o conteúdo da classe em dois arquivos (talvez porque um é escrito à mão e o outro gerado por um modelo), e dizer que ambos os arquivos definem os membros essenciais da classe com igual importância, e não exigem que um arquivo "extender" a classe definida na outra.

É uma diferença quase existencial, mas talvez importante para algumas pessoas em algumas situações.

Eu não quero parcial nas classes pelas razões indicadas, mas nos módulos faz sentido IMHO.

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

Classes parciais são extremamente úteis para especializar casos de uso. Não é uma violação de qualquer padrão OOP, mas uma maneira de organizar o documento de código, que fornece:

  1. Capacidade de dividir a definição de classe / tipo em vários arquivos. Para arquivos de código enormes, é um recurso de manutenção ++.
  2. Oportunidade para o compilador / sistema de construção fazer uso da ligação condicional em tempo de compilação. Analogia MSBuild, caso de uso concreto em C #: DBML, classes de designer e coisas ainda mais sofisticadas como https://github.com/dotnet/corefx/pull/2045/files.
  3. Proteção inerente, para que dois (ou mais) desenvolvedores não se encontrem durante o check-in do código. A resolução de conflitos é um golpe! :)
  4. Permite gerar código na classe parcial principal e, em seguida, criar outra parcial para sobrecargas personalizadas. Funciona perfeitamente!

Eu acredito em separar a lógica de negócios e as camadas de dados em classes separadas e uso principalmente métodos estáticos nessas camadas. Eu realmente não vejo como poderia ser mais código e menos sustentável.

Enquanto o método de extensão é um recurso central da linguagem influenciado por padrões OO e será processado em tempo de compilação, as classes parciais lidam com o layout do documento / código a ser costurado no estágio de pré-compilação. Apesar das semelhanças em classes parciais, classes abstratas e métodos de extensão, há uma distinção clara.

: +1: por trazer as inofensivas "Classes parciais" para o TypeScript.

Meu caso de uso para classes parciais é para geração de código - minha equipe precisa ser capaz de gerar automaticamente parte da classe em um arquivo e adicionar métodos e propriedades personalizados manualmente em outro arquivo. Estou um pouco confuso por que @RyanCavanaugh "não tem certeza se esse tipo de situação se aplica a JavaScript / TypeScript", já que esse caso de uso não está relacionado à linguagem de escolha.

Nem decoradores nem métodos de extensão resolvem o problema de geração de código tão elegantemente quanto classes parciais, e cada um tem complexidades adicionais substanciais.

+1

+1

+1 para fins de geração de código.

No ambiente C #, a integração de um transpiler de classes C # às classes TypeScript eliminaria a necessidade de manter duas definições diferentes de classes entre o lado do servidor e do cliente, já que o lado do cliente, também conhecido como classe TypeScript, seria gerado automaticamente e a classe parcial que esse problema está solicitando permitiria a personalização quando necessário, enquanto ainda desenvolve em TypeScript nativo.

+1 para cenários de geração de código

+1 extremamente útil para geração de código

Por que não usar herança para geração de código?

A herança é mais complicada e tem mais sobrecarga.

Na segunda-feira, 10 de agosto de 2015, 10:09, Gorgi Kosev [email protected] escreveu:

Por que não usar herança para geração de código?

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129468291
.

A herança cria acoplamentos de nomes inúteis

A herança é menos impressionante porque:

  1. Aprofunda a cadeia de protótipos desnecessariamente e retarda a instanciação,
    embora isso não seja um grande problema para a maioria dos aplicativos, provavelmente tudo, exceto jogos;
  2. Por falta de resumo e substituição, ou perde algum tipo de segurança ou
    introduz a necessidade de alguns padrões desagradáveis, como fundir entre os dois
    tipos;
  3. É confuso no geral, pois ter uma classe base comunica a ideia de que
    pode ser estendido por outras classes, o que raramente é o caso com codegen

Não dizer datilografado TEM que ter classes parciais para suportar codegen, apenas
que classes parciais são muito melhores nisso do que herança ou
composição.

@JoshMcCullough A sobrecarga de herança em JS é mínima, e não vejo como é mais confusa do que classes parciais: com elas, você pode potencialmente dividir uma classe em 10 arquivos e perseguir seus métodos em todo o lugar.

@ yahiko00 De que tipo de "combinação de nomes inútil" estamos falando aqui? Você se refere às coisas que precisa fazer para obter o nº 2 abaixo (codificando o nome da classe estendida)?

@hdachev

  1. Há alguma sobrecarga, mas é mínima - só perceptível se você estiver fazendo coisas que se traduzem diretamente em algumas instruções de máquina
  2. Bom ponto - você não obtém a classe estendida quando precisa fazer referência à classe gerada de outras classes geradas.
  3. Essas classes realmente foram feitas para serem estendidas - caso contrário, você poderia gerá-las e terminar com isso, certo?

AFAIK isso não está no radar ES7 +, não é? Isso pode ser problemático ...

Em qualquer caso, anexar coisas extras ao protótipo de uma classe existente é bastante trivial em JavaScript e nada incomum, então pode ser uma boa ideia ter uma sintaxe para isso ...

É simples no TS, mas o JS nos bastidores dobraria o
"protoyping" se você estivesse usando classes básicas para todos os seus dados gerados
objetos. Provavelmente não é um impacto sério no desempenho, a menos que você tenha uma tonelada de
objetos de dados, mas ainda é desnecessário quando sabemos que "parcial
classes "são possíveis.

Na segunda-feira, 10 de agosto de 2015 às 11h03 Gorgi Kosev [email protected]
escreveu:

@JoshMcCullough https://github.com/JoshMcCullough Sobrecarga de herança
em JS é mínimo, e não vejo como é mais confuso do que classes parciais:
com eles você pode potencialmente dividir uma classe em 10 arquivos e perseguir seu
métodos em todo o lugar.

@ yahiko00 https://github.com/yahiko00 Que tipo de "nome inútil
acoplamento "estamos falando aqui? Você se refere às coisas de que precisa
que fazer para obter o nº 2 abaixo (codificar o nome da classe estendida)?

class MyClass extends GeneratedMyClass {...}

@hdachev https://github.com/hdachev

1

Há alguma sobrecarga, mas é mínima
https://jsperf.com/prototype-chain-vs-direct-calls - apenas perceptível
se você está fazendo coisas que se traduzem diretamente em algumas máquinas
instruções
2

Bom ponto - você não tem a aula estendida quando precisa
referencie a classe gerada a partir de outras classes geradas.
3

Essas aulas são de fato feitas para serem estendidas - caso contrário, você poderia
gerá-los e pronto, certo?

AFAIK isso não está no radar ES7 +, não é?

Em qualquer caso, anexar coisas extras ao protótipo de uma classe existente
é bastante trivial em JavaScript e nada incomum, por isso pode ser um
boa ideia ter sintaxe para isso ...

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129483174
.

@ Davidhanson90 esse arquivo não tem classes nele. Você quis dizer postar isso em um tópico diferente?

+1

+1

+100

+1

+1

1 para geração de código.
Tenho entrado no angularjs e webapi e quero fazer uma ferramenta que essencialmente cria definições JS automáticas de meus objetos e serviços c #. Desejo então ser capaz de estender essas classes sem ter que editar as definições de JS "scaffolded". Percebi que algumas outras pessoas estão solicitando isso e parece que temos casos de uso semelhantes.

+1 para geração de código

+1 para geração de código, angular tem muito código clichê, já estamos gerando bastante, mas com classes parciais poderíamos fazer muito mais.

+1 mais uma vez para geração de código. Tentar estender classes em outro arquivo é, na melhor das hipóteses, confuso.

@RyanCavanaugh , o que é necessário para tirar isso do modo "+1" e avançar em uma direção produtiva?

-1 a seguinte sintaxe é melhor para mixins: # 2919

Não sei por que não podemos ter classes parciais e mixins; dois recursos totalmente não relacionados.

+1

Isso seria bom quando o código é gerado, mas você não quer modificá-lo para adicionar coisas ao código gerado.

+1

Tenho uma proposta que deve atender a esses dois casos de uso:

  1. Você deseja escrever métodos de sua própria classe em locais diferentes.
  2. Você deseja adicionar novos métodos à classe de outra pessoa, por exemplo, um builtin.

Para (2), normalmente você usaria apenas o operador :: para obter a sintaxe do infixo.

Mas se você quiser que uma classe antiga faça parte de uma nova interface, ou se quiser despachar dinamicamente, você precisará realmente modificar o protótipo.

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 {}
}

(No exemplo, eu uso nomes de métodos de string, mas como esses são tipos internos, o uso de símbolos seria melhor, uma vez que eles podem ser verificados pelo tipo.)

Uma melhoria que isso tem em relação às verdadeiras classes parciais é que a classe tem uma única definição e outros lugares apenas a estendem. Isso significa que há um lugar claro para importar a classe e torna a tradução para JS mais fácil.

: +1: Eu adoraria ver aulas parciais também.

Qual é o status deste recurso?

Este caso de uso, por exemplo:

Eu tenho um pacote Math que define uma classe Set , com métodos Set#add , 'Set # remove'

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

Eu tenho um pacote opcional pesado Relational que define as classes Relation e Tuple .

Este pacote Relational também adicionaria um método Set#product .

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

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

Aulas parciais me permitiriam compor aulas de uma forma mais flexível. Se eu não quiser usar o pacote pesado relational , ainda posso usar a funcionalidade Set básica. Outras partial classes apenas virão e adicionarão funcionalidades extras à classe.

Estou coçando minha cabeça para fazer algo que terá uma boa API sem o uso de parciais neste caso.

Tudo o que consegui é algum padrão de repositório.

// 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 Estou usando t4 para gerar classes e proxies sobre WebApi. Eu acho que a geração automática está usando muito na digitação e quando se trata de geração automática, classes parciais são necessárias!

Seria ótimo para dividir aspectos da mesma classe, especialmente no React
+1

+1 Geração de código e "separação apenas o suficiente" no caso de objetos de dados reutilizáveis ​​puros com atributos específicos do cliente para validação semelhante que seria legal ter definido na classe, mas mantido em uma parte separada do código-fonte.

+1 para geração de código de classes parciais e código manual de membros adicionais para essas classes em arquivos separados.

+1

+1 Eu tenho uma grande base de métodos em nosso serviço js api, seria melhor dividi-los em arquivos, o que torna mais fácil entender cada método.

serviço de API como fb (facebook) ou gq (google analytics) , onde você recebe uma classe ou objeto global, que pode ser usado em todo o seu desenvolvimento.

+1

Esse recurso seria uma grande vantagem para nós também.

Desenvolvemos um middleware onde todos os tipos de clientes podem se conectar e se comunicar com nossos servidores através dele.

Geramos parte do código do cliente com base no que é exposto pelos servidores.

Até agora estava tudo bem. Mas agora, gostaríamos de poder transferir coleções heterogêneas de objetos (mas com a mesma classe base). Os objetos transferidos fazem parte do código gerado com base na API do servidor.

Para poder usar o poder da herança, os métodos abstratos são a chave neste caso. Mas nossos desenvolvedores não vão adicionar métodos ao código gerado, presumo que todos saibam por quê;)

Pelo que entendi (sou um desenvolvedor C #), a solução proposta aqui (https://github.com/Microsoft/TypeScript/issues/9) não nos permitiria fazer isso.

Então, aulas parciais seriam perfeitas para nós. Geraríamos a classe como parcial e, se os desenvolvedores precisassem adicionar lógica ou métodos, eles teriam apenas que escrever uma outra parte onde desejassem.

+1

+1 muito útil para geração de código

Eu me pergunto se o aumento do módulo basicamente nega a necessidade disso como um recurso específico.

Se você tem uma classe gerada, você deve ser capaz de fazer 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 Essa parece ser uma boa maneira de implementá-la nos

Eu concordo com o Elephant-Vessel. A parte criada manualmente da classe parcial deve ser o mais fracamente acoplada (em tempo de design em TS) quanto possível da parte gerada.

A sugestão de @david-driscoll funciona para muitos casos. No entanto, pelo que posso dizer, os protótipos não têm acesso a variáveis ​​internas. Isso limita a utilidade dessa abordagem em contextos de geração de código em que você deseja gerar as partes principais de suas classes e, em seguida, aumentá-las com código customizado.

+1

@david-driscoll que parece uma excelente maneira de desugar esse recurso. (precisaria adicionar a capacidade de acesso a propriedades privadas / protegidas). Gostaria de saber o que aconteceria com várias declarações de classe parcial? Qual seria a visibilidade mútua deles?

++ 1 A falta de parcial está realmente me prejudicando ao tentar estender meus modelos gerados automaticamente.

Acho que a única coisa em escopo aqui é basicamente o que @ david-driscoll propôs - você poderia declarar novos métodos (que seriam colocados no protótipo) e novas propriedades _non-initialized_ (que não têm codegen), mas não novas propriedades inicializadas (porque eles têm efeitos colaterais no codegen do construtor).

voto negativo, desculpe meu francês, aulas parciais são uma tentativa estúpida de dividir suas classes divinas, que são inevitáveis ​​em OOP, em vários arquivos para que sejam mais fáceis de gerenciar (embora ainda sejam classes divinas)

com OOP não há futuro para vocês (já estive lá, sabe do que estou falando)

venha para o FP, temos cookies e uma maneira de escrever um programa sem uma única classe e s ** t assim

Então, basicamente, já permitimos isso de qualquer maneira por meio de interface A { method(): void } A.prototype.method = function() { }; ; codificar isso em um açúcar exatamente para isso faz sentido.

Uma coisa para andar de bicicleta é a palavra-chave partial . Uma breve vinheta histórica: em C #, partial class seria originalmente extension class (que você colocou em todas as declarações, exceto uma), mas não havia uma distinção clara sobre quais declarações seriam as extension e qual declaração seria a declaração de não extensão. Em vez disso, você tem o modificador partial que deve ser colocado em _todas as classes.

_Não_ é o caso no TypeScript; pelo contrário. Aqui, apenas uma classe (vamos chamá-la de "declaração _primária_") pode ter construtores / campos de membro inicializados; a declaração primária não recebe nenhum novo modificador. Todas as outras declarações (vamos chamá-las de "declarações de _extensão_") podem ter apenas estáticos, métodos e campos não inicializados, e você _precisa_ ter algum modificador neles para indicar que não são primários.

O melhor palpite atual é

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

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

: brilha:: bicicleta:: casa:: brilha:

Bikeshedding ou não, isso está soando horrível como o # 311, que foi abandonado devido à provável interferência com os padrões futuros do ECMAScript. Por que vale a pena considerar isso, mas o suporte adequado ao mixin não é?

Usar a palavra-chave extension conflita com métodos de extensão ? Ou isso vai negar completamente os métodos de extensão?

@kitsonk Acho que a proposta do mixin tem muito mais questões em aberto. A proposta de classe parcial aqui é apenas uma codificação de coisas que já são permitidas no TypeScript, apenas com um pouco de açúcar sintático (esse recurso poderia ser feito literalmente como um remapeamento sintático para coisas que já temos).

@ Elephant-Vessel Eu não acho que métodos de extensão estão acontecendo se classes parciais acontecerem. As classes são o único lugar onde os métodos de extensão fazem sentido, dadas as nossas restrições, e o operador de ligação ES7 + proposto é a melhor maneira de usar a sintaxe semelhante à extensão.

@RyanCavanaugh : para mim, parece um pouco constrangedor exigir que uma classe seja primária e as outras não. Se você deseja particionar seu código de modo que um arquivo contenha todos os métodos de uma classe e o outro contenha todas as propriedades, nenhum dos dois parece mais obviamente primário, e por que você deveria ser forçado a escolher? Não é mais limpo ter parciais onde são todos iguais em importância e mesclados pelo compilador?

@RyanCavanaugh ... Eu faria exatamente da mesma maneira que c # faz ... funciona muito bem para geração de código ... que acredito ser o principal problema que o recurso pretendia resolver em c # ... embora minha memória seja confusa ... nunca teve problemas, existe há anos e foi testado em batalhas ...

Se classes parciais / estendidas não puderem ter campos inicializados, acho que seria mais legal oferecer suporte a algo como métodos parciais que conhecemos do C # , basicamente um contrato leve específico para classes parciais. Assim como:

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

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

A necessidade de ser capaz de parcializar / estender classes de terceiros poderia ser facilmente (?) Resolvida tornando partial/extends geralmente opcional, mas obrigatório para o uso de métodos parciais.

Eu acho que isso poderia trazer algum poder expressivo e operativo puro para o conceito que queremos aqui.

Aqueles que procuram mixins.

Considere a proposta de inicialização do

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

Linguagens como Java não oferecem classes / estruturas parciais. É um recurso de nicho do C #. Imitar os conceitos de "classe / estrutura parcial" do C # em toda a extensão é a melhor maneira de prosseguir aqui.
Na etapa de pré-compilação, a primeira coisa que o compilador TS pode fazer é costurar os blocos de código parciais na memória e, em seguida, passar para a rota do pipeline de compilação regular. Isso será mais do que suficiente para se qualificar para v1.0.0-preview1.0000 do recurso Classes parciais no TypeScript.
Qualquer coisa além do que o C # está oferecendo é algo que pode ser introduzido em versões posteriores, quando o recurso evoluir e sobreviver a sua versão de teste / visualização. Os casos extremos e coisas obscuras semelhantes podem ser discutidas separadamente, um de cada vez, e podemos nos preocupar com o encaixe e o acabamento mais tarde ... IMO.

Também concordo em fazer aulas parciais e de extensão exatamente da mesma maneira que c #
é a melhor maneira de fazer em termos de sintaxe e semântica, ambas funcionam
extremamente bem para seus respectivos casos de uso. Aulas parciais melhoram o
usabilidade do código gerado. Métodos de extensão melhoram a usabilidade
de métodos estáticos que operam em interfaces, genéricos especificados e terceiros
tipos de festa. Ambos são muito legais, mas lembre-se de que eles não são
mesmo recurso, estou dizendo isso aqui por causa da sintaxe proposta acima.

Métodos de extensão permitiriam fazer isso (reclamar sobre a falta de formatação,
digitando em um telefone):

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

extend WeirdThirdPartyDatastructure{
mesmo aqui() { ... }
}

Isso, entre outras coisas, como ser capaz de usar bibliotecas como o sublinhado
sem o cruft, o que basicamente significa colher os benefícios sintáticos de
estendendo tipos integrados sem realmente poluí-los.

Tão breve - não vamos tratar os dois como um e o mesmo, eles são muito
legal, mas não a mesma coisa. Eu pessoalmente adoraria ter ambos em nosso
língua.

A propósito, como observação lateral, imagino que muitas pessoas se assustem com a ideia de
geração de código e estão prontos para explicar a todos que não há lugar
em javascript porque existem maneiras mais idiomáticas de conseguir o mesmo.
Existem duas maneiras pelas quais a geração de código é de grande ajuda em alguns
projetos, como o que estou trabalhando agora.

  • Em projetos sensíveis ao desempenho, como jogos, onde você apenas faz
    tudo para maximizar seu orçamento de CPU com trabalho útil, o codegen ajuda muito
    para gerar um código que seja simples de raciocinar e altamente otimizável
    / código monomórfico que se encaixa muito bem. Por exemplo, em um
    mecanismo de jogo de entidade / componente, o código gerado pode ajudar com uma tonelada de coisas
    como um código clichê muito rápido para unir componentes, evento
    despacho, etc.
  • Em aplicativos de grandes negócios, o código gerado ajuda a obter uma extrema quilometragem de
    o sistema de tipo, por exemplo, emitindo métodos com métodos muito específicos
    assinaturas para consultas de banco de dados, etc, o que maximiza o tempo de compilação
    verifica, ajuda muito com a refatoração e também simplifica muito a depuração
    porque permite percorrer um código de orquestração muito simples.
  • O grande problema é quando você mantém algum componente em sincronia
    ambientes com vários idiomas, por exemplo, algo relacionado a mensagens
    como buffers de protocolo ou apenas interfaces de mensagem json, etc.

Como outro nó lateral para o benefício de quem está se perguntando o que
classes têm a ver com codegen, o valor de manter as partes geradas
de uma classe em um arquivo separado vem dos requisitos de controle de origem -
o material gerado é geralmente um artefato de construção que muda rapidamente e você
não quer que seja comprometido em seu repo.

Gosto da ideia de implementar isso de acordo com um modelo bem conhecido e testado desse conceito, assim como o C #. 'Imitação' tem algumas conotações negativas, mas tem um valor significativo se feito de forma inteligente. Por outro lado, a parte 'inteligente' disso é estar ciente de diferentes circunstâncias e limitações, então não protestarei se as parciais para TypeScript não se parecerem exatamente com parciais para C #.

@ Elephant-Vessel, concordo que não precisa ser uma cópia idem de parciais C #, o design geral pode ser traçado primeiro em torno do tipo TypeScript / JavaScript enquanto se inspira ao máximo nos parciais C #. Minha sugestão é ir com 'registrar partial palavra-chave' e 'costurar parciais' como um primeiro passo, e enviá-lo como um recurso experimental / de visualização (para que o consumidor não comece a depender dele no código de produção certo De cara). Mais tarde, com base na resposta da comunidade, desenvolva o recurso até que esteja pronto para produção e RTM. Se nos preocuparmos com todos os tipos de pegadinhas e cenários complicados de antemão, provavelmente isso atrasará ainda mais o assunto.

+1 - Para estender e manter os objetos gerados.

+1 para manter o código gerado

Eu criei polyfill e dividi minha turma grande.

Defina uma classe como uma interface.

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

Implemente os membros da classe.

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

Mesclar implementações.

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

Eu acho que esta é uma ideia terrível por um motivo específico: ela tornará as regras já complicadas para problemas de herança dependente de ordem de declaração global, ambiente, externo, com espaço de nomes e frágil significativamente piores. Isso não é C # e a resolução de nomes e as declarações de membros são muito diferentes.

Talvez use um objeto ou namespace em vez de uma classe, como o padrão de módulo revelador, se você realmente precisar fazer isso. Caso contrário, sua classe provavelmente é muito grande.

Além disso, decoradores podem ser usados ​​para implementar associações entre o código gerado e o código escrito à mão.

Você tem outras soluções?

Eu adoraria ver isso adicionado ao TS também

+1 para fins de geração de código (DTOs de uma referência de serviço WCF com definições de classes parciais adicionais)

+1 para fins de geração de código. Meu caso de uso é: tenho componentes React cujas funções render() são geradas externamente por meio de react-templates .

Com partial classes eu poderia verificar o tipo de tais funções versus a classe principal (a Component ). Sem classes parciais, a única coisa que posso fazer é vincular a função à classe principal e esperar o melhor.

Com o próximo TS 2.0, eu estava pensando que alguns casos de geração de código poderiam ser cobertos com o recurso do tipo this .

Por exemplo, no caso que descrevi anteriormente, em vez de

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

eu consigo escrever

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

+1 Como desenvolvedor, quero classes parciais para que vários modelos possam gerar / modificar a mesma classe espalhada por vários arquivos.

Meus comentários foram especificamente em relação às aulas parciais, não aos mixins. Os dois são fundamentalmente diferentes. Classes parciais são para quebrar o código físico, mixins são para quebrar o comportamento lógico em características reutilizáveis ​​que enriquecem outros objetos em nível de tipo e valor.
Não acho que classes parciais sejam uma boa ideia para o TypeScript. Mixins, por outro lado, são uma boa ideia, proporcionando maior expressividade e combinando extremamente bem tanto com o sistema de tipos do TypeScript quanto com os idiomas JavaScript, conforme apontado por @aleksey-bykov

@wendellm, você pode pensar em uma maneira limpa de fazer isso, visto que os Módulos ES são físicos. As classes parciais funcionam bem e fazem muito sentido em uma linguagem como C #, onde os módulos são lógicos e não físicos. Do ponto de vista do CLR, os namespaces não existem, apenas os nomes das classes podem conter pontos (fonte de uma entrevista com @ahejlsberg)

1 eu preciso de aula parcial!

podemos interface de extensão? interface também pode ter algumas funções implementadas, assim como Swift:

interface Rect {
    x: number
    y: number
}

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

Versão Swift:

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

+1

A geração de código e as classes parciais andam de mãos dadas.

Herança é uma solução pobre para o que é essencialmente uma diretiva #include glorificada.

Tome Angular 2 por exemplo. Ele não lerá metadados da classe pai, tornando a extensão por herança impraticável.

@tsvetomir é um problema do Angular, não do TypeScript.

Herança é uma solução pobre para o que é essencialmente uma diretiva #include glorificada

Sim, herança é uma solução ruim, mas o problema é usar classes em primeiro lugar. Eles têm o seu lugar, mas é muito limitado. As classes de JavaScript são muito fracas e inexpressivas em comparação com as classes de outras linguagens.

O texto datilografado não é uma linguagem "Angular" ... Nunca foi

Em 23 de julho de 2016, às 21h46, Aluan Haddad < [email protected] [email protected] > escreveu:

@tsvet omirhttps: //github.com/tsvetomir esse é um problema Angular, não um problema TypeScript.

Herança é uma solução pobre para o que é essencialmente uma diretiva #include glorificada

Sim, herança é uma solução ruim, mas o problema é usar classes em primeiro lugar. Eles têm o seu lugar, mas é muito limitado. As classes de JavaScript são muito fracas e inexpressivas em comparação com as classes de outras linguagens.

Você está recebendo isso porque comentou.
Responda a este e-mail diretamente, visualize-o em Gi tHubhttps: //github.com/Microsoft/TypeScript/issues/563#issuecomment -234753589 ou silencie o th readhttps: //github.com/notifications/unsubscribe-auth/AJPCIh7n2_0dt00kw- XJv7tc9LB0tPsIks5qYtH4gaJpZM4CcixK.

Não vejo o problema como específico do Angular de forma alguma. É um caso particular em que aulas parciais podem ter ajudado.

A solicitação se origina de experiências anteriores com C # e da falta de recursos familiares. A comparação é inevitável, pois o TypeScript não faz nenhum esforço para se distanciar do .NET. Alguma forma de paridade de recursos é esperada por seus usuários.

A solicitação se origina de experiências anteriores com C # e da falta de recursos familiares. A comparação é inevitável, pois o TypeScript não faz nenhum esforço para se distanciar do .NET. Alguma forma de paridade de recursos é esperada por seus usuários.

@tsvetomir como um desenvolvedor .NET de longa data e como um programador C # apaixonado, eu discordo fundamentalmente.

O artigo que você citou não é oficial e não reflete nenhum material oficial da equipe do TypeScript.

Além disso, ele contradiz diretamente a filosofia de design do TypeScript conforme declarado por @ahejlsberg em várias palestras, entrevistas e postagens.

Além disso, o ponto de vista do artigo citado reflete um mal-entendido fundamental quanto à natureza do TypeScript.
Ironicamente, os pontos comuns que o C # tem com o TypeScript são, na verdade, os mesmos que tem com o JavaScript.
Essas semelhanças não são programação baseada em classe, mas sim construções como fechamentos e funções de ordem superior.

C # ou não, precisamos de classes parciais que ajudarão o TypeScript a se aproximar dos recursos de protótipo do Javascript.

@ yahiko00
O TypeScript já possui todos os recursos de protótipo do JavaScript.
É um superconjunto.

Existem vários problemas com a noção de classes parciais no TypeScript, incluindo

  1. Ao contrário do C #, as definições de classe do TypeScript (JavaScript) são imperativas, não declarativas. Eles parecem declarativos, mas não são. Isso significa que eles dependem de uma ordem de execução determinística.
  2. O sistema do módulo ECMAScript é baseado na importação de unidades de código físicas e não lógicas.
    Por exemplo, digamos que tenho

_app / my-class-part-1.ts_

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

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

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

Como posso importar esta classe? Como não posso importar de nenhum dos módulos, vamos imaginar uma abstração hipotética no TypeScript que me permite escrever
_app / main.ts_

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

O que isso significa? Mesmo se o compilador TypeScript pudesse inferir que _app / my-class-part-1.ts_ deve ser carregado antes de _app / my-class-part-2.ts_, ele não pode invalidar a importação de partes individuais nem garantir eles são importados na ordem correta por um carregador de módulo assíncrono como RequireJS ou o que será eventualmente implementado em navegadores.

Toda a noção de classes parciais está fundamentalmente em desacordo com o sistema de módulos ECMAScript.

Atualização: seria necessário realizar inferência de dependência arbitrariamente complexa e emitir um JavaScript muito estranho.

O TypeScript já possui todos os recursos de protótipo do JavaScript.
É um superconjunto.

Eu deveria ter me expressado melhor. Claro que todos nós sabemos que o TypeScript é um superconjunto do JavaScript. Mas eu queria dizer que as classes parciais ajudariam as classes do TypeScript a se aproximarem dos protótipos JS. Por enquanto, podemos apenas ter classes "estáticas" e não extensíveis, enquanto, com uma abordagem de protótipo, podemos estender funções semelhantes a classes.

A importação de classes parciais levaria a estende a sintaxe ES6 atual. Eu concordo. Podemos imaginar algo assim:

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

As classes TypeScript são baseadas em classes ECMAScript e, portanto, sofrem com a falta de expressividade das últimas. Considere o uso de funções, módulos, "namespaces" ou objetos simples para realizar o que você precisa.

Funções é o que queremos evitar com a noção de classe.
Módulos ou namespaces trazem extensibilidade simples e não têm a mesma expressividade que as classes do TypeScript. Além disso, um namespace ou módulo não pode ser instanciado. Eles não têm o mesmo propósito que as aulas.

@aluanhaddad Eu vejo o que você quer dizer com relação aos módulos. Uma possível solução seria permitir que apenas um dos arquivos export partial class enquanto o resto contém apenas partial class definições. Você o importa do local em que foi exportado. Do seu exemplo:

_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';

O que está faltando é uma sintaxe para permitir que o compilador saiba onde localizar as partes. Isso não é um problema com C #, em que você processa todos os arquivos de uma vez. Esta pode ser uma sintaxe especial, como a usada para fusão de declaração .

Não é necessário que as classes parciais permitam definições arbitrárias. Eles podem ser limitados apenas a métodos, propriedades e construtores, por exemplo. Isso torna a ordem de execução insignificante e permite que o compilador emita um erro se um membro for duplicado.

Não consigo ver por que o aumento do módulo não é válido para esses cenários. Funcionou muito bem para nós com Rxjs5.

Você simplesmente cria sua classe base e, em seguida, com sua geração de código, gera todos os métodos aumentados de que precisa no topo da classe base. Não importa se o código gerado é bonito, nenhum ser humano deve escrevê-lo.

É um substituto perfeito para aulas parciais? Suponho que não, mas não vejo classes parciais acontecendo a menos que o ECMAScript decida que o JavaScript precisa delas. A semântica de como as classes parciais são ligadas entre si entre módulos externos dói minha cabeça.

@ david-driscoll para torná-lo ainda melhor, métodos adicionados podem ter um argumento this: declarado, para uma verificação de tipo mais forte.

No exemplo citado de Aumento do

// 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
}

Sim. Rxjs ainda não o usa, porque é um recurso 2.0, e 2.0 ainda não foi lançado. Está na minha lista de alvos mais tarde.

Eu vejo o que você quer dizer com relação aos módulos. Uma possível solução seria permitir que apenas um dos arquivos exportasse a classe parcial, enquanto o restante conteria apenas as definições parciais da classe. Você o importa do local em que foi exportado. Do seu exemplo:

_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;
    }
}

Eu gosto do conceito sintático, mas o problema é que você acabou de introduzir um novo _tipo_ de arquivo de origem que é consumido como uma espécie de módulo implícito, parece um script e deve ser carregado de uma maneira específica.

O que está faltando é uma sintaxe para permitir que o compilador saiba onde localizar as partes. Isso não é um problema com C #, em que você processa todos os arquivos de uma vez. Esta pode ser uma sintaxe especial, como a usada para fusão de declaração.

A mesclagem de declarações funciona para _declarações_, não para arquivos de origem.

Não é necessário que as classes parciais permitam definições arbitrárias. Eles podem ser limitados apenas a métodos, propriedades e construtores, por exemplo. Isso torna a ordem de execução insignificante e permite que o compilador emita um erro se um membro for duplicado.

Todos estes _são_ comumente dependentes do pedido.

Edit: Isso quer dizer que defini-los modifica a classe.

Provavelmente alguém já mencionou isso, mas outro caso útil é quando você gera parte da classe a partir de algum gerador de código, então você deseja adicionar manualmente mais algum código diretamente em sua implementação.
Você realmente não deseja fazer mixagem ou classe derivada.
Ainda é uma classe - apenas uma parte é gerada automaticamente outra parte manualmente.

BTW: se alguém precisa gerar tipos de TS a partir de classes C #, você pode verificar TypeScriptBuilder

Obrigado por considerar isso!

Vou experimentar o seu TypeScriptBuilder;)

Minha sugestão...

E se o compilador não tentasse encontrar todas as partes? E se eu tivesse que importar outras peças e controlar tudo?

/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;
    }
}

Então...

  1. Se eu quiser usar MyClass , tenho que importá-lo de MyClass.ts
  2. Quando compilado, MyClass.partial.ts irá gerar um javascript que estende MyClass.prototype como mostrado aqui antes. Mas ele exporta uma função que recebe o protótipo a ser estendido.
  3. MyClass.partial.ts é importado em MyClass após MyClass ter sido definido e sua função de extensão ser chamada.

Em uma nota lateral ... não há nada que me impeça de gerar o código compilado diretamente. Mas eu perco toda a grandeza do Dactilografado.

@svallory Acho que essa é definitivamente a abordagem certa aqui. Especificamente porque você diz que o código gerado exportará uma função que causaria o aumento, ele funcionaria mesmo com importações assíncronas porque as funções podem ser chamadas na ordem determinística necessária.

De forma mais geral, e esqueço o problema em que foi referenciado, se as atribuições para o protótipo da classe afetassem a forma da classe, poderia haver muitos benefícios. Isso melhoraria as bibliotecas mixin, tornaria os decoradores muito mais úteis e geralmente encorajaria menos hierarquias verticais.

Pode ser complicado para o compilador rastrear tudo isso, mas provavelmente poderia funcionar sendo explícito como você sugere.

Seria um aprimoramento para o sistema de tipos, com o caso de uso de classes parciais caindo fora desse aprimoramento.

Eu fiz assim:

Eu fiz assim :
 arquivo1 . ts
 interface ifoo {
 a ( ) : vazio ;
 }
 classe foo implementa ifoo {
 a ( ) { / * faça algo * / }
 }
 arquivo2 . ts
 /// <reference path = "file1.ts" /> // não necessário
 interface ifoo {
 b ( ) : vazio ;
 }
 foo . protótipo . b = ( ) = > { / * faça algo * / }
 arquivo3 . ts
 /// <reference path = "file1.ts" /> // não necessário
 interface ifoo {
 c ( ) : vazio ;
 }
 ( < ifoo > foo . protótipo ) . c = ( ) = > { / * fazer algo * / }
 consumidor . ts
 /// <caminho de referência = "arquivo1.ts" />
 /// <caminho de referência = "arquivo2.ts" />
 /// <caminho de referência = "file3.ts" />
 deixe f = novo foo ( ) ;
 f . a ( ) ;
 f . b ( ) ;
 f . c ( ) ;

O aumento do módulo ainda resolve 90% do problema aqui. Dados os exemplos 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;
    }
}

O aumento do módulo seria algo 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;
    }
}

É um pouco mais detalhado do que uma classe parcial, mas na verdade a classe parcial seria apenas um açúcar sintático para essa implementação.

As únicas pegadinhas que você não tem hoje são:

  • Você não pode acessar campos privados ou protegidos usando a sintaxe this: MyClass .

    • Isso pode ser facilmente resolvido no código gerado lançando para any .

    • Talvez seja algo que possa ser adicionado, de modo que você possa acessar membros protegidos de this . Pode haver outras regras em torno disso.

  • O código gerado parece um pouco mais intimidante, mas para o consumidor, uma vez que as coisas foram aumentadas, ele nunca saberia a diferença.

EDIT: Tenha em mente que esta é a sintaxe do TS 2.0+.

@ david-driscoll eu concordo. Acho que é mais simples e gosto que não introduza a sintaxe adicional. Como você sabe, me oponho a classes parciais como um recurso de linguagem específico, mas acho que seria benéfico se o TypeScript rastreasse atribuições a protótipos em geral e refinasse a forma do objeto de acordo. Acho que sua abordagem é a maneira certa de dividir classes em vários arquivos.

@wongchichong Seu exemplo usa nomes de classes globais e /// <reference path="..."/> . Eu não acho que escalará bem, embora você possa usar um namespace para melhorar a situação.
O código escrito como módulos (ou seja, módulos externos) é uma besta muito diferente porque envolve um carregador que precisa estar ciente das dependências que são dependentes da ordem e implícitas. É por isso que a sugestão de

E se alguém não usar módulos?

@pankleks classes parciais ainda são uma má ideia.

Para esclarecer, quero dizer que eles são uma má ideia em linguagem como JavaScript, onde as declarações de classe são imperativas, não declarativas. Mesmo se estiver usando namespaces, você vai acabar quebrando seu código em vários arquivos, o que significa que terá que depender da ordem da tag de script desses arquivos para que isso funcione.

Em uma linguagem como C #, as classes parciais funcionam bem, mas isso ocorre porque são fundamentalmente diferentes das classes em JavaScript.

É importante lembrar que, em JavaScript, as declarações de classe nem mesmo são levantadas, muito menos declarativas.

Exceto ES6, o conceito de "classe" é definido por TS. JS não tem noção de uma classe _at all_.
Portanto, cabe ao TypeScript decidir o que uma classe pode ser, não?

Exceto ES6,

Não podemos barrar o ES6 ao discutir os recursos da linguagem TS, porque um dos objetivos do TS é seguir as especificações ES atuais / futuras .

Verdade isso :/

Para grandes sistemas com classes geradas por código, ou seja, entidades, clientes da camada de serviço, etc., classes parciais de acordo com o design C #, fornecem uma solução muito elegante quando o código gerado precisa ser estendido com estado e comportamento adicionais. No meu projeto atual, onde temos cerca de 500 classes de geração de código, sinto muita falta desse recurso.

Ainda não entendo por que algumas pessoas são tão hostis contra um recurso que se mostrou útil em outro lugar, exceto dizendo "é uma má ideia".

Parece que isso não foi muito discutido no esdiscuss, o único tópico que encontrei tem 9 posts.

@ yahiko00 Não é que eu seja hostil ao conceito. Como você disse, é muito útil em linguagens como C #.

Como um programador C # entusiasmado, considero as classes parciais muito úteis para certas tarefas. No entanto, eles não interferem nos outros aspectos da linguagem. Eles não quebram abstrações relacionadas a chaves, como namespaces, assemblies e declarações de tipo arbitrariamente ordenáveis.

JavaScript, por outro lado, possui um sistema de módulos que é, na melhor das hipóteses, incipiente. As aulas são uma novidade e, eu diria, como ainda não são uma característica muito expressiva da linguagem, precisam de tempo para crescer.

Se dermos um passo para trás por um momento e considerarmos os métodos de extensão, outro recurso poderoso e muito mais útil do C #, eu adoraria vê-los adicionados ao JavaScript, mas há problemas fundamentais com isso que ainda precisam ser resolvidos.

Quando chegarmos a um ponto em que classes parciais possam ser especificadas sem quebrar ou restringir severamente conceitos muito mais fundamentais, como módulos, serei totalmente a favor de adicioná-los.

Dito isso, como @SaschaNaz aponta, isso _não_ precisa ser tratado no ECMAScript.

Talvez qualquer pessoa interessada possa abrir um novo tópico em http://esdiscuss.org e postar a URL aqui para que possamos continuar a discussão lá 😄

PS: Ou no ES Discourse, mais parecido com o GitHub.
PS2: Ou no WICG também semelhante ao GitHub e mais ativo.

Eu também adoraria ver alguma construção que permita a geração de código para TS. Classes parciais funcionam muito bem para isso em C #.

Talvez eu esteja faltando alguma coisa, mas me parece que o principal problema é estender uma classe com acesso a variáveis ​​'privadas' de algum lugar diferente do construtor, de preferência de outro arquivo.

Com base na discussão, parece que muitas das inovações 'seguras' de TS já foram feitas. Agora estamos apenas esperando para ver o que o órgão de governança ES faz, porque não queremos quebrar nada daqui para frente. Totalmente válido, mas um pouco desanimador porque eu esperava um trem em movimento mais rápido com TS.

Um pré-requisito é encontrar uma boa remoção de açúcar :

Embora eu tenha pensado anteriormente que classes parciais eram muito bobas porque você poderia simplesmente adicionar à classe diretamente, posso ver o apelo, já que fazer isso envolverá tornar seus métodos não enumeráveis, além de corrigir a superligação (a última das quais é atualmente impossível em ES6).

Primeiro, precisamos descobrir um bom desguaring para classes em geral em primitivos imperativos de composição, como o antigo toMethod. Mas, uma vez que temos isso, adicionar um pouco mais de açúcar no topo como classes parciais pode ser razoável, dependendo de quão ergonômicos - ou não - esses primitivos acabem sendo.

Não tenho certeza do que isso significa exatamente, mas provavelmente assim:

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

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

(Cross postado em esdiscuss )

Parece que há uma maneira bastante simples de implementar uma classe parcial em JS puro (com ES2017 getOwnPropertyDescriptors ). As pessoas do ES podem pensar que um açúcar de sintaxe não será necessário apenas para remover esse código minúsculo.

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 isso no TS requer uma duplicata interface para estender o tipo de classe existente, é claro.

@SaschaNaz Espero que as propriedades adicionadas ao construtor por meio de getOwnPropertyDescriptors sejam ocultadas para "auto-sugestão" do IDE (já que são adicionadas no tempo de execução). Lembre-se, o Typescript nasceu como um JavaScript com "auto-sugestão" (às vezes as pessoas chamam de "fortemente tipado", mas na verdade o que eles gostam é de "auto-sugestão").

PS Por que as pessoas precisam de classes parciais - codegeneração. Mas, novamente, a codegeneração deve ser suportada tanto por IDE ("ferramenta" fácil de ajustar ao arquivo de origem, resultado hierárquico em explorador de arquivos, T4 ou suporte semelhante a "codegenerador comum") e em linguagem (classes parciais, métodos de extensão). Só depois de entender isso é possível entender o que os desenvolvedores realmente perguntam. Se você não vê a imagem completa - é claro que "aulas parciais" parece "um absurdo".

@aluanhaddad : mas a função principal do TypeScript não é nos fornecer elementos de linguagem que não estão presentes em JavaScript? Por que é uma má ideia ter uma classe dividida em dois arquivos TypeScript parciais, que serão compilados em um único arquivo JavaScript?

Eu realmente não vejo problema em dar uma ferramenta para a comunidade. E tenho 100% de certeza de que haverá desenvolvedores que farão mau uso dele. Mas também desenvolvedores que sabem exatamente como usá-lo. Mas não temos a mesma situação com qualquer construção de qualquer linguagem?

+1 para fins de geração de código. E concordo com @rpokrovskij , porque escolhemos TypeScript, também posso usar JavsScript diretamente, só porque parece C # e tem suporte ide.

@greendimka Eu não sei como isso poderia funcionar sem divergir e adicionar uma complexidade substancial aos Módulos ES.

@ xiexin36 TypeScript com certeza parece mais com JavaScript do que com C # para mim.
Existem semelhanças entre JavaScript e C #, mas suas noções de class estão em mundos diferentes.

@ xiexin36 para geração de código, você ainda pode usar o aumento do módulo (como observei anteriormente neste tópico ). Sintaticamente, não é tão "fácil" quanto as classes parciais, mas você pode realizar a mesma tarefa.

@aluanhaddad Bem, funciona de maneira muito simples: classes parciais não alteram a estrutura do código de nenhuma maneira. Eles apenas nos permitem escrever a definição da mesma classe em vários arquivos (que são compilados em um único JS de qualquer maneira).

@aluanhaddad Estritamente falando: javascript não tem conceito de classes (classes ES6 apenas fornecem um açúcar sintático). Portanto, se tentarmos seguir o conceito de ter o TypeScript tão fraco quanto o JavaScript, não deveríamos jogar fora as classes do TypeScript? Não deveríamos simplesmente reverter para o JavaScript?

As classes
O TypeScript não tem como objetivo adicionar construções adicionais ao tempo de execução do JavaScript.

As classes ES6 são _muito_ sem brilho. Isso não significa que JavaScript não seja uma linguagem poderosa (é). O TypeScript fornece digitação estática para detectar erros em tempo de design, fornecer ferramentas de alta qualidade, permitir especificações extremamente precisas e rigorosas de interfaces de componentes e, em geral, melhorar a produtividade.

As classes ES6 são _muito_ sem brilho, mas o TypeScript introduzir sua própria noção de classes seria uma violação direta de seus objetivos de design.

Simplesmente evite as aulas e seu problema será resolvido.

Bem, funciona de forma muito simples: classes parciais não alteram a estrutura do código de nenhuma maneira. Eles apenas nos permitem escrever a definição da mesma classe em vários arquivos (que são compilados em um único JS de qualquer maneira).

E quebrar quase todas as ferramentas conhecidas pelo homem? Não, obrigado.

@ david-driscoll Obrigado, vou tentar, mas também uso Angular 2.0, gostaria de saber se gerei código com
@Input ou @Output , vai funcionar, de qualquer forma vou tentar.

@aluanhaddad é uma pena ver como você se pendurou em repetir "classes? javascript não tem classes". Remova classes de sua cabeça, deixe apenas JavaScript e agora ouça: os desenvolvedores que usam geração de código querem ter uma unidade de compilação em vários arquivos. Não existem classes nesta linguagem, apenas funções? OK, queremos funções parciais. E o JavaScript está quase pronto para isso: as funções lá podem ser usadas antes da declaração. Lá podemos encontrar outros problemas, é claro (por exemplo, ter certeza de que todas as partes / arquivos são carregados no ambiente de execução), mas eles são solucionáveis ​​e, na verdade, queremos ter unidades de compilação parcial em TypeScript, não em JavaScrpipt. O texto datilografado está pronto para este 100%.

E, a propósito, equipe do TypeScript, forneça-nos funções parciais também! E diga o mesmo para a equipe C #, já que temos lá "funções em funções" por enquanto. :)

Só para ter certeza, você percebeu que as funções parciais têm um significado muito específico e o que você está descrevendo não são funções parciais por nenhum esforço da imaginação?
A geração de código é útil, eu também a uso.

@aluanhaddad . Você entende o termo "uma unidade de compilação em vários arquivos"? É claro que Javascript não é uma linguagem de compilação, mas a) a função deve ser totalmente analisada antes da execução e podemos chamar isso de "compilação" b) O Typescript é uma linguagem de compilação.

Para ajudar sua imaginação:

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

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

Eu também não uso classes parciais com frequência: para usá-las efetivamente precisamos armazenar os parâmetros de geração de código na parte "manual" e para ler essa configuração devemos ter um bom analisador de linguagem (não reflexão). Mas é assim: classes parciais são apenas um instrumento entre muitos para tornar a codegeneração confortável.

@rpokrovskij
Peço desculpas se pareci antagônico. Não quis ofender ou desrespeitar.

Seu exemplo tem aspectos que exigiriam uma descrição bastante formal por si só. Esta proposta não era originalmente sobre a ativação direta de cenários de geração de código.

@aluanhaddad ....
“Simplesmente evite as aulas e seu problema será resolvido” - sério, isso é solução para um problema ?! Provavelmente, se eu evitar o uso de JavaScript, posso resolver o problema de toda essa discussão. Também é uma solução, não é? Infelizmente, temos essa linguagem de merda em nosso universo porque ela teve a sorte de entrar na web há muitos anos. De qualquer forma, este é um offtopic.

"quebrar quase todas as ferramentas conhecidas pelo homem" - como isso é possível ??? Quão?!?! Tento imaginar quebrar algo usando classes parciais no TypeScript, mas não consigo. Simplesmente não posso. Por favor, ajude-me aqui com algum exemplo da vida real.

Agora imagine que você construiu um carro para transportar caixas. Funciona sem problemas. Você contrata um trabalhador, que carrega o carro. O trabalhador pega duas caixas por vez e as coloca no carro. Ainda assim, tudo funciona bem.
Um dia o trabalhador vai embora para outra empresa e você contrata uma nova. O novo pega uma única caixa, carrega, pega outra caixa e carrega também. Logicamente: está tudo bem. Mas você corre e grita: pare, pare, temos uma falha de sistema! :)

“Simplesmente evite as aulas e seu problema será resolvido” - sério, isso é solução para um problema ?! Provavelmente, se eu evitar o uso de JavaScript, posso resolver o problema de toda essa discussão. Também é uma solução, não é? Infelizmente, temos essa linguagem de merda em nosso universo porque ela teve a sorte de entrar na web há muitos anos. De qualquer forma, este é um offtopic.

O ES2015 tem muitas coisas erradas com ele, com certeza. ES5.1 tinha muitas coisas erradas com ele, com certeza. Na minha opinião, falta de aula não era uma dessas coisas. Meu ponto é que, ao usar técnicas como o padrão de módulo revelador, você elimina a necessidade de classes parciais. TypeScript fornece açúcar para o padrão de módulo revelador, namespaces.

"quebrar quase todas as ferramentas conhecidas pelo homem" - como isso é possível ??? Quão?!?! Tento imaginar quebrar algo usando classes parciais no TypeScript, mas não consigo. Simplesmente não posso. Por favor, ajude-me aqui com algum exemplo da vida real.

Se você usar Módulos ES, isso é problemático. Qualquer ferramenta que dependa de artefatos físicos do compilador tendo uma correspondência com suas contrapartes não compiladas estaria sujeita a isso. Dito isso, o TypeScript poderia se ajustar a isso na emissão, uma grande mudança, mas então estaria emitindo JavaScript que não correspondia ao TypeScript que você escreveu.

Então, basicamente, a evolução de uma linguagem está sendo travada pelas ferramentas, que nunca irão evoluir sem a evolução da linguagem.
Desculpe, mas vejo aqui apenas a busca pelo motivo para não evoluir.

Quais são os benefícios que as aulas parciais oferecem, que você já não pode fazer com o idioma de uma maneira diferente? É um bom açúcar sintático, com certeza, mas quais são os verdadeiros ganhos? Existe um exemplo concreto além de torná-lo mais parecido com C #?


À parte: para que fique registrado, comecei como desenvolvedor C # e, a partir daí, mudei para JavaScript / C #. Depois que o TypeScript 0.8 foi anunciado, fui vendido, não porque era C # para JavaScript, mas porque estava pegando JavaScript estrito e o tornando melhor para desenvolvedores que apreciam os tipos fortes de C #.

O TypeScript não é C # e não deve herdar estritamente recursos do C # apenas porque o C # os possui. Assim como o C # não deve herdar recursos do TypeScript. Não consigo contar quantas vezes quis recursos como tipos de união em C # porque eles são incríveis para TypeScript, mas ainda não são adequados para a linguagem.


Até agora eu li Geração de código e organização de código.

Geração de código, olhe para Aumento do módulo, há algumas deficiências que mencionei aqui, mas tenho certeza que podem ser corrigidas.

Organização de código, acho que usar regiões (risos)? Existem muitas outras maneiras de organizar seu código, se você tiver uma classe God (por qualquer motivo), você sempre pode dividi-lo em muitas classes God. Superficialmente, a classe god é a importação que todos veem e usam, mas, por baixo do pano, ela é composta de muitas classes, talvez como propriedades da classe god. Se você estiver usando classes parciais para organização, eu diria que você precisa refatorar seu código um pouco para tentar SOLID ify seu código.

Consegui ajustar o aumento na maioria dos cenários de geração de código que encontrei até hoje.

Para Omnisharp-Client, eu uso a extensão de interface aqui, onde OmniSharp.Api.V2 é gerado por meio de reflexão em C #. Na parte inferior do arquivo, tenho uma ferramenta que exclui todos os métodos dessa interface.

Para RxJS, usamos para mapear todos os nossos métodos que estão em Observable .

Eu simplesmente não consigo raciocinar em torno de alguns dos problemas com aulas parciais. Por exemplo, se tivermos 3 classes parciais.

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

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

A compilação global é onde isso parece ser mais fácil. Como o TypeScript já mescla os namespaces.

Se meu tsconfig.json contiver apenas os caminhos de arquivo para Class1 e Class2, então tudo o que estiver em Class3 não será automaticamente mesclado.

  • A princípio, isso parece um erro do usuário, mas não ficará imediatamente aparente por que os métodos da Class3 não estão incluídos. Este é um grande problema de usabilidade que levará à confusão.

Escopo: Módulos Externos (importação / exportação)

Com a compilação externa, cada arquivo é realmente considerado um módulo (ou Hell Assembly no mundo .NET) porque cada módulo tem uma lista explícita de todas as suas dependências (importações) e todas as suas APIs públicas (exportações).

Como você raciocina sobre esses cenários?

Dada apenas uma referência de arquivo, qual é o comportamento da classe?
Ele mescla Class2 e Class3 automaticamente?

  • A definição padrão de Class1, Class2 e Class3 exporta exatamente o mesmo valor?
  • Qual arquivo é emitido para o disco?
  • Qual será a aparência do JavaScript subjacente para todos os formatos suportados (amd, umd, commonjs, etc)
// somefile.ts
import { MyAwesomeClass } from 'MyAwesomeClass1';

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

Precisamos importar explicitamente todas as instâncias da classe? (Se fizermos isso anula o propósito imo).
O que acontece quando você importa tudo em um arquivo, mas não em outro, para os métodos cary over?
Isso pode levar a erros realmente estranhos, dado um gráfico de dependência complexo onde a classe é usada uma vez, sem as parciais extras. Os valores seriam aumentados? ou os métodos não funcionariam magicamente até que seja carregado o outro arquivo que os traz?

// 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 Apenas meus dois centavos aqui:

É um bom açúcar sintático, com certeza, mas quais são os verdadeiros ganhos?

Não é um bom açúcar sintático o ponto principal de qualquer linguagem? De um ponto de vista operacional, tudo o que precisamos são loops, cláusulas if e iteração para fazer tudo. O resto é apenas um _bom açúcar sintático_ para ser mais produtivo e fornecer outras qualidades não funcionais semelhantes.

E embora as linguagens sejam geralmente boas na parte operacional de instruir um computador, geralmente sugam a parte organizacional de escrever código. Temos esses artefatos culturais ad-hoc como objetos, classes, herança e outros, mas nenhuma base teórica sólida, afaik, que realmente explique o domínio das operações de organização e todas as abstrações que estamos tentando construir e representar em nosso código-fonte para para poder trabalhar com isso de uma maneira boa.

Há duas coisas que adoro no TypeScript: 1. Ele nos permite usar javascript digitado para que, de repente, seja gerenciável construir sistemas complexos no cliente. 2. O sistema de tipos é extremamente expressivo (porque foi forçado a expressar todos os tipos de padrões em javascript que as pessoas pudessem escrever sem a limitação de um sistema de tipos).

Mas em relação à estruturação do código, não acho que seja significativamente menos ruim do que qualquer outra linguagem. E eu gostaria que pudéssemos ser mais inovadores aqui. Dê aos usuários mais liberdade para experimentar a parte organizacional da linguagem. Abrir-se e buscar uma nova expressividade estrutural em vez de ser restritivo e desconfiado.

As classes parciais não são apenas muito convenientes para a geração de código especificamente, mas aumentam a liberdade na organização em geral. Liberdade de que precisamos para sair deste buraco de organização de código.

@ david-driscoll qual é o ponto de ter parciais? Bem, mais uma vez: geração de código.
O que há de errado com a solução proposta? É uma solução alternativa. Mais uma solução alternativa (e uma solução bastante suja). Já temos uma linguagem de merda (javascrtipt), que está repleta de soluções alternativas. Em vez de pensar em suas tarefas principais, os desenvolvedores de javascript passam muito tempo pensando nas soluções alternativas. Com sua filosofia proposta, podemos descartar toda a linguagem TypeScript. Porque (surpresa, surpresa) todo o sistema de tipos pode ser simulado em javascript puro - usando soluções alternativas. Por que se preocupar com uma linguagem mais expressiva (como Typescript) se você pode simular qualquer coisa com soluções alternativas? Por que escrever uma única linha de código quando a mesma coisa pode ser escrita em vinte linhas? Traga de volta os salários com base em várias linhas de código escritas! Código curto e expressivo é para maricas, não é?

E não: a solicitação de parciais no TypeScript não é baseada na ideia de que o C # os possui. Não acredito que o TypeScript deva ter algo apenas pelo fato de ter, porque outra linguagem tem.

E finalmente: por que você acha que se você não usa algo - ninguém precisa?

@greendimka Eu não estava tentando provocar nenhuma hostilidade: escudo :

Agora, lembre-se de que não faço parte da equipe do TypeScript, sou apenas um defensor, mas a equipe tem estado bastante silenciosa aqui. A equipe pode muito bem intervir quando tiver algo específico a dizer, e vou adiá-la se assim decidirem.

Um dos principais inquilinos que a equipe do TypeScript tem mantido é tentar manter o TypeScript em movimento em sincronia com a direção do ECMAScript e evitar fazer quaisquer recursos específicos que possam fazer com que eles divergam da direção geral para a qual o JavaScript está evoluindo.

Não é que eu não o usasse (provavelmente usaria se existisse). O problema é como está neste problema classes parciais podem advertências, portanto, parece duvidoso que será implementado em breve. Provavelmente ficará assim por um tempo até que a) se torne um recurso obrigatório para uma parte interessada de alto valor ou b) uma proposta vá para os estágios posteriores do TC39.

Não vejo o aumento do módulo como uma solução alternativa. Ele permite que você aumente o sistema de tipos e permite muitos dos cenários de geração de código que foram detalhados neste encadeamento. Este é um passo em direção a outros recursos de linguagem.

  • É feio? Absolutamente, feio como o inferno. Dito isso, se você está gerando o código, qual é a diferença? Apenas sua ferramenta de geração precisa se preocupar em como aumentar a classe. Depois de executar sua ferramenta de geração de código e incluir seus módulos, você obtém erros completos de intellisense e de compilação, etc.

  • É perfeito? Longe disso! Eu vejo um cenário muito mais comum com mixins precisando de suporte no nível de idioma. E eu acho que algo parecido com decoradores como proposto aqui faz sentido, embora novamente não haja nenhuma proposta específica.

@ david-driscoll, desculpe por ser extremamente rude. Eu realmente tendo a estar do lado do progresso positivo.

Eu gostaria disso para poder colocar implementações estáticas em uma classe parcial.

Visto que não parece que isso acontecerá em breve, gostaria de propor uma solução alternativa que encontramos. Existem alguns comentários como este, mas eu não vi nenhum com essa leitura específica.

Gostaríamos que os Partials fossem usados ​​em um cenário de geração de código em que desejamos ser capazes de estender nosso código TypeScript gerado. No entanto, estamos gerando modelos que têm interdependências, portanto, herdar de nossas classes não funciona bem no sentido tradicional. As classes geradas que fazem referência a outras classes não terminam com o tipo certo de objeto sendo referenciado e acabaríamos com um padrão de fábrica complexo para obter os tipos de instância corretos criados.

Acabamos mudando nossas classes geradas para classes base, mas apenas na declaração da classe. Em seguida, criamos classes stub que herdamos de nossas novas classes base. Poderíamos então estender essas novas classes e elas herdariam todo o seu conteúdo da classe base.

Por exemplo:
Se gerássemos uma classe Person, agora geraríamos um PersonBase. Todo o código gerado vai nesta classe. Também geraríamos uma classe vazia Person que estende PersonBase. Essa classe só seria gerada uma vez. Em seguida, colocamos todo o nosso código gerado em PersonBase e todo o código personalizado vai manualmente para Person. Todas as referências geradas para Person permanecem como Person e não PersonBase. Dessa forma, todo o IntelliSense continua funcionando.

Arquivo 1: Classe Base

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

Arquivo 2: Classe real

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

Isso resolveu nosso problema de classe parcial com o código gerado. Parabéns a Andrew Scott por ter tido a ideia.

Eu também gostaria muito disso, já que estou gerando alguns códigos com um gerador de código e preciso adicionar algumas alterações a ele. Classes parciais permitirão regenerar o código sem ter que adicionar as alterações novamente mais tarde (que podem ser grandes ou complexas)

Há uma razão importante para ter classes parciais que eu acho que ainda não foi mencionada: migração gradual de JS para TS. Em Javascript, particularmente pré-ES6, as classes podem se parecer com um monte de:

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

E eles podem ser espalhados por vários arquivos sem nenhum tratamento especial. A conversão de uma base de código como essa em TypeScript moderno atualmente requer funções móveis para que todos os membros de uma classe estejam no mesmo arquivo, o que pode ser uma alteração bastante invasiva. Com aulas parciais, a migração poderia ser feita usando apenas edições locais.

@RyanCavanaugh Você mencionou que tudo o que é solicitado aqui pode ser tratado sintaticamente com outra sintaxe TypeScript:

Acho que a proposta do mixin tem muito mais questões em aberto. A proposta de classe parcial aqui é apenas uma codificação de coisas que já são permitidas no TypeScript, apenas com um pouco de açúcar sintático (esse recurso poderia ser feito literalmente como um remapeamento sintático para coisas que já temos).

Então, como alguém faria isso no TypeScript ?:
File1.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;
}

File2.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 o acima iria emitir uma classe JS para Contact, algo assim:

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;
})();

Observe que no final eu quero uma classe chamada Contact e não quero ter que criar uma subclasse de duas classes separadas em uma terceira.

@cosmoKenney

Aqui está um exemplo de algo que funcionará. Não é sintaticamente alterado como partial mas funciona e é uma solução muito 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, você também pode gerar a interface e usar a extensão da interface para implementá-la.

Também pode haver maneiras de usar a nova funcionalidade do Mixin, no entanto, ainda não me concentrei nisso o suficiente para dar uma resposta qualificada.

@ david-driscoll Uau. Minha cabeça explodiu.

Eu me pergunto se a comunidade vai acabar com um fork, que simplesmente oferece suporte a classes parciais sem apagar um problema com isso.
Até agora não ouvi nenhum argumento real contra aulas parciais. Apenas alguns motivos para justificar uma preguiça.

@greendimka

Eu me pergunto se a comunidade vai acabar com um fork, que simplesmente oferece suporte a classes parciais sem apagar um problema com isso.
Até agora não ouvi nenhum argumento real contra aulas parciais. Apenas alguns motivos para justificar uma preguiça.

Não tenho certeza do que você está tentando dizer aqui. Mas se o sentimento geral dentro da comunidade é que podemos alcançar os mesmos resultados usando os recursos de linguagem existentes, então preciso apenas de alguns exemplos de como fazer isso. O que @ david-driscoll postou acima está além da minha compreensão. E não parece resolver completamente o problema. Na verdade, pelo que posso dizer, ele interpretou mal minhas intenções.
Minha principal preocupação é gerar código para as propriedades escalares da classe, como um pojo ou classe de modelo. Mas eu preciso ser capaz de, em um arquivo separado, ou mesmo no mesmo arquivo, mas não dentro da definição de classe primária, adicionar as propriedades de associação (para que eu possa colar uma definição primária recém-gerada por código sobre a original).
Por exemplo, minha classe de contato acima obteria o código gerado com firstName e lastName. Mas, em minha definição separada é onde eu adicionaria a definição e a inicialização da coleção de objetos Address.
Meu gerador de código conhece apenas os atributos da classe, não as associações, então ele não pode codificar a coleção de endereços - como foi feito no exemplo de David.

@cosmoKenney não estava totalmente claro onde o endereço deveria existir. Perdoe-me por entender o contexto errado. Estou apenas tentando ajudar a dar alternativas. Existem muitas funcionalidades que permitem um comportamento semelhante sem colocar algo no compilador, como classes parciais, que têm muitas considerações adicionais e problemas potenciais para implementar ambos os sistemas de módulo (interno / externo).

No seu caso, gerar uma classe base abstrata, ou geração de interface funcionaria, então sua outra classe herda disso. Então, o fato de que sua classe foi gerada ainda está oculto.

ie:

// 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 oferece uma solução interessante, mas ainda carece da facilidade de aulas parciais reais. Como meus experimentos pessoais mostraram, os desenvolvedores simplesmente não usam "parciais" que são feitos dessa maneira. Embora seja uma boa solução alternativa, a facilidade e o conforto dos parciais não estão lá.

No momento, não sei como o compilador TypeScript funciona internamente. Só posso supor que ele lê todos os arquivos de origem, os prossegue e gera arquivos JS. Se for assim, não vejo problemas em ler uma única classe de vários arquivos de origem. Se dois ou mais arquivos de origem de uma única classe fornecem código conflitante (por exemplo, mesma propriedade definida duas vezes), o compilador simplesmente lança uma exceção. Da mesma forma que acontece se você escrever código sintaticamente inválido.

@greendimka , @david-driscoll
Tentei compilar o exemplo de David acima, mas não gostou:

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.

Além disso, concordo com @greendimka que isso é muito complexo para usar centenas de vezes em um aplicativo grande.

@greendimka Eu também não sei como o compilador funciona, mas não subestime a tarefa. JavaScript não é .NET e os módulos não estão disponíveis globalmente (você deve importá-los literalmente)

Adoraria ver isso implementado, mas essa tarefa traz várias coisas que precisam ser pensadas com cuidado, como:

  • Se você definir parte da classe em um arquivo A, parte no arquivo B, qual deve conter a classe completa após a tranpilação? Um novo deve ser gerado?
  • Todos os arquivos que contêm itens parciais devem exportar a mesma classe?
  • Como evitar dependências cíclicas se eu exigir o arquivo principal nesses adicionais?
  • E se eu exportar mais coisas no módulo do que apenas a própria classe?
  • Verificação de conflitos de nome?

e assim por diante...

@greendimka Veja minhas respostas abaixo: (EDITAR: foi direcionado a @WoLfulus , desculpe @greendimka):

Se você definir parte da classe em um arquivo A, parte no arquivo B, qual deve conter a classe completa após a tranpilação? Um novo deve ser gerado?

Eu ficaria perfeitamente feliz se o arquivo A.js tivesse a definição completa e o arquivo B fosse retirado. Talvez com um comentário enlatado. A maioria de nós está agrupando de qualquer maneira, então a miríade de arquivos .js agrupados torna-se essencialmente uma reflexão tardia no final.

Todos os arquivos que contêm itens parciais devem exportar a mesma classe?

Isso foi praticamente respondido pela minha resposta à primeira pergunta.

Como evitar dependências cíclicas se eu exigir o arquivo principal nesses adicionais?

Eu não sei. Não é tarefa do carregador de módulo reconhecer esse cenário?

E se eu exportar mais coisas no módulo do que apenas a própria classe?

Não vejo problema nisso. Em outras palavras, se B.ts tem uma definição parcial e algumas outras classes não parciais, então B.js deve conter o comentário pronto como mencionado acima E as definições das classes não parciais. Simples, eu acho.

Verificação de conflitos de nome?

Como você terminaria com um conflito de nomes? Se você quer dizer sobre a classe, então a palavra-chave "parcial" resolveria isso. Mas então, se dois outros arquivos definirem classes com o mesmo nome, mas sem a palavra-chave "parcial", o compilador deve emitir um erro sobre tipos duplicados - como faz agora. Mas se você estiver falando sobre duas definições parciais da mesma classe definindo uma propriedade ou método com o mesmo nome, o compilador deve emitir um erro sobre membros duplicados - como faz agora.

@WoLfulus , @cosmoKenney respondeu bem. Só para torná-lo melhor com sua primeira pergunta (sobre os arquivos A e B): como designer de linguagem, tem-se o privilégio de definir regras. Com isso quero dizer que o arquivo A pode ter esse código "classe X parcial" e o arquivo B pode ter esse código "classe X parcial (fileX)", que produzirá o arquivoX.js.

@cosmoKenney

Como evitar dependências cíclicas se eu exigir o arquivo principal nesses adicionais?
Eu não sei. Não é tarefa do carregador de módulo reconhecer esse cenário?

TypeScript não possui um carregador de módulo.

@greendimka

Eu me pergunto se a comunidade vai acabar com um fork, que simplesmente oferece suporte a classes parciais sem apagar um problema com isso.
Até agora não ouvi nenhum argumento real contra aulas parciais. Apenas alguns motivos para justificar uma preguiça.

Isso não é uma boa ideia. Por exemplo, você considerou como manterá sua bifurcação quando as importações dinâmicas forem adicionadas?
Além disso, muitos argumentos foram feitos contra classes parciais. Quebrar a correspondência de origem para origem entre os arquivos TypeScript e JavaScript vai contra um princípio fundamental da linguagem. Imagino que um recurso precisaria possibilitar um cenário infinitamente mais valioso do que classes parciais para que isso pudesse ser considerado. Provavelmente ainda seria rejeitado.

@WoLfulus , @cosmoKenney respondeu bem. Só para torná-lo melhor com sua primeira pergunta (sobre os arquivos A e B): como designer de linguagem, tem-se o privilégio de definir regras. Com isso quero dizer que o arquivo A pode ter esse código "classe X parcial" e o arquivo B pode ter esse código "classe X parcial (fileX)", que produzirá o arquivoX.js.

Eu não acho que você está transmitindo sua mensagem. Isso faz parecer que você sente que as limitações que você já encontrou para se expressar em qualquer idioma existem por razões totalmente arbitrárias.
Além disso, as perguntas de

dons flamesuit

TL; DR: Isso não está acontecendo. Se você está aqui no comentário número 189 e deseja registrar sua desaprovação com isso, leia o tópico inteiro primeiro, porque as boas razões para não adicioná-lo foram amplamente abordadas acima.

Nossa interpretação desta situação é a seguinte:

  • TypeScript tem muitos recursos de classe específicos de TS (declarações de propriedade de construtor, inicializadores de membro, decoradores não finalizados, modificadores de acesso, implements , etc.) para algo que realmente pretendia ser apenas "Tipos ES6 +" . Parte disso é culpa nossa (nossa direção projeto original era geralmente muito OOP) e parte disso é culpa de ES6 (a proposta classe mudei muito há cinco anos e acabamos tendo características que não fazê-lo). Adicionar mais uma característica de classe específica de TS é outro canudo nas costas do camelo que devemos evitar se pudermos.
  • Sem ambigüidades, o futuro do JavaScript são os módulos. Classes parciais só fazem sentido e funcionam bem no modelo de "sopa global". Permitir classes parciais abrangendo vários módulos é uma receita para o desastre, pois a ordem de carregamento não é previsível. Adicionar um recurso que só funciona bem em um universo global não é um bom uso do tempo.
  • C # tem, mas não estamos aqui para reinventar o C #. Seriamente!
  • O cenário de "código gerado" não é convincente quanto aos méritos. Qualquer emissão plausível para classes parciais restringiria os inicializadores de propriedade e o código do construtor a exatamente uma declaração, e isso seria uma solução para as configurações do estilo .Designer.cs pois tanto o código do usuário quanto o código gerado provavelmente precisariam de inicializadores ou lógica do construtor .
  • Existem muitos, muitos outros mecanismos de composição disponíveis em JS - mixins, Object.assign , etc. O cenário que levou a esse recurso em C # foi impulsionado por restrições que simplesmente não estão presentes em JS.
  • A solução atual de interface + prototype.method = ... não permitir que o cenário de código gerado tão bem quanto partial class faria. É estranho escrever esse código manualmente, mas não há problema em gerar código de forma diferente, então se você realmente acha que uma configuração de código gerado por arquivo dividido é a única maneira de resolver seu problema (sou cético), então esse método está disponível para você .
  • Consideramos ES6 como o árbitro de "obrigatória" para as classes. Não há nada de especial no TypeScript (além de seu apelo para programadores C #) que o faça precisar de classes parciais mais do que o JavaScript não tipado. Portanto, se houver algum cenário que realmente o derrube para adicionar classes parciais, então esse cenário deve ser capaz de se justificar por meio do processo TC39. Se isso ganhar força, ficaríamos felizes em contribuir com informações sobre ele, mas isso nem parece estar no radar de ninguém.

@RyanCavanaugh sobre isso:

A solução alternativa atual de interface + prototype.method = ... habilita o cenário de código gerado tão bem quanto a classe parcial faria.

Como já mencionei mais de uma vez, não há problema em haver uma maneira não parcial e não c # de realizar o que preciso e apenas pedir um exemplo prático. david-driscoll foi muito útil, mas postou código que não compila. Portanto, se você acha que posso gerar um código de classe com propriedades escalares apenas para, por exemplo, nome, sobrenome - como acima - tudo bem. Eu só preciso de uma maneira de adicionar uma coleção de outros tipos como uma propriedade na classe, codificando isso manualmente. Mas também preciso de uma maneira de lidar com a inicialização desse conjunto.

@cosmoKenney seu site copiou o código certo? O segundo exemplo tinha interfaces em vez do mesmo nome de classe. Posso anexar um CEP do código, se quiser. Eu estava hesitante porque arquivos aleatórios na internet podem ser ruins e não quero que você pense que eu estava postando algo malicioso 🙂

Lamento ouvir isso, mas eu respeito as decisões de design feitas aqui. Tenho algumas perguntas se estiver tudo bem:

O TypeScript já tem muitos recursos de classe específicos de TS (declarações de propriedade do construtor, inicializadores de membro, decoradores não finalizados, modificadores de acesso, implementos, etc.) para algo que realmente pretendia ser apenas "Tipos ES6 +". Parte disso é nossa culpa (nossa direção de design original geralmente era muito OOP) e parte disso é culpa do ES6 (a proposta de classe mudou há cinco anos e acabamos tendo recursos que não funcionaram). Adicionar mais uma característica de classe específica de TS é outro canudo nas costas do camelo que devemos evitar se pudermos.

Considerando isso, um caminho possível seria distinguir claramente, organizacionalmente e em termos de código, entre a tipagem pura de JS e as outras construções de linguagem que o TS oferece? Eu, pelo menos, amo tanto a digitação quanto os recursos extras de linguagem. Como consumidor / desenvolvedor, não me importo muito com o fato de o TS adicionar coisas que não existem no JS. Apenas me dê coisas incríveis, se TC39 não quiser, eu não me importo.

Se o TS encapsular essas coisas entre si, talvez isso seja mais fácil de reutilizar e ser bifurcado e construído em projetos como SoundScript, em vez de substituído?

E então outro projeto poderia se basear nessa tipificação para fornecer melhores / outras construções de linguagem do que as oferecidas por JS.

Sem ambigüidades, o futuro do JavaScript são os módulos. Classes parciais só fazem sentido e funcionam bem no modelo de "sopa global". Permitir classes parciais abrangendo vários módulos é uma receita para o desastre, pois a ordem de carregamento não é previsível. Adicionar um recurso que só funciona bem em um universo global não é um bom uso do tempo.

É realmente? Claro que os módulos seriam uma parte natural do JavaScript, mas e quanto ao corpo do código original principal? Eu amo namspaces e não há sopa nele se for feito corretamente. Estou compilando tudo em um arquivo e não preciso de carregadores de módulo e, em quase todos os casos, não preciso de carregamento de módulo assíncrono. Algumas coisas propostas como https://github.com/Microsoft/TypeScript/issues/420 só seriam possíveis e relevantes com namespaces. Você está dizendo que coisas assim não vão acontecer porque só devemos usar módulos? Os namespaces estão se tornando obsoletos?

@ david-driscoll Obrigado, você já foi útil o suficiente. Visto que @RyanCavanaugh é quem fica fechando o problema e afirmando que existem soluções alternativas, talvez ele deva fornecer um exemplo de

Também estou vendo muitos conceitos errados sobre o que são classes parciais. É realmente uma construção simples, e todo mundo está exagerando:
https://msdn.microsoft.com/en-us/library/wa80x488.aspx

Considerando isso, um caminho possível seria distinguir claramente, organizacionalmente e em termos de código, entre a tipagem pura de JS e as outras construções de linguagem que o TS oferece? Eu, pelo menos amo tanto a digitação quanto os recursos extras de linguagem. Como consumidor / desenvolvedor, não me importo muito com o fato de o TS adicionar coisas que não existem no JS. Apenas me dê coisas incríveis, se TC39 não quiser, eu não me importo.

Isso seria reescrever uma linguagem e quebrar o código de todos.

Além disso, o "Just give me awesome stuff" é fundamentalmente muito diferente dos objetivos de design para TypeScript . Se você quer apenas coisas incríveis e não se preocupa com o alinhamento com JavaScript, deve escolher uma linguagem diferente, como CoffeeScript , Dart ou Haxe . É claro que talvez haja motivos pelos quais eles não estejam indo tão bem quanto o TypeScript na adoção ...

alguém que fica fechando o problema e afirmando que existem soluções, talvez ele deva fornecer um exemplo de solução

Existem muitos casos de uso para o que partial resolve. Quase qualquer construção de linguagem tem muitos casos de uso para os quais é válida, mas por boas razões, você tem que escolher e escolher.

Você continua pedindo um recurso de linguagem específico, como em "Eu adoraria um carro verde", mas não está chegando ao cerne da questão, por que o carro precisa ser verde? Se você explicou por que seu carro precisa ser verde, ou esteja aberto ao fato de que você realmente não se importa com a cor do seu carro, desde que atenda às suas necessidades reais.

Não vejo por que você acha que é obrigação de Ryan lhe dizer como repintar seu carro, só porque, por motivos válidos, a montadora decidiu não produzir carros verdes.

(Ah, e as classes mixin adicionadas no TypeScript 2.2 são uma boa maneira de realizar o que suspeito ser sua real necessidade)

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();

@kitsonk mixins não são a mesma coisa.

  • Mixins são unidades independentes que podem ser adicionadas às classes existentes.
  • Classes parciais podem acessar membros de qualquer outra classe parcial, como se fossem parte da mesma classe.

Como exercício, tente implementar a seguinte divisão de código com 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 exercício, tente implementar a seguinte divisão de código com mixins

Então, de volta ao ponto ... Então, em vez de apontar um recurso de linguagem, que problema você está tentando resolver com seu exemplo? Porque a solução alternativa no TypeScript é 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);
  }
}

(e se formos completistas, mas, novamente, ele resolve um uso, nem todos os 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);

@ Elephant-Vessel

Considerando isso, um caminho possível seria distinguir claramente, organizacionalmente e em termos de código, entre a tipagem pura de JS e as outras construções de linguagem que o TS oferece?

Não vejo uma maneira de separar os dois neste momento. Alguém poderia, em teoria, escrever alguma linguagem ala CoffeeScript que se transformou sintaticamente em TypeScript e usar essa linguagem de nível superior para adicionar novos recursos sintáticos enquanto ainda usa o sistema de tipos do TS, mas isso só levaria você até aí. Apenas escolher uma das muitas outras linguagens de compilação para JS inexperientes, em vez disso, parece uma escolha melhor se esse for o objetivo.

Você está dizendo que coisas assim não vão acontecer porque só devemos usar módulos? Os namespaces estão se tornando obsoletos?

Os namespaces não vão a lugar nenhum, com certeza. Eles são apenas a nomenclatura de um padrão que já está em uso generalizado e ainda fazem muito sentido em um mundo de módulos ES6. Os módulos são cada vez mais a forma como as pessoas organizam seu código e têm muitos benefícios colaterais excelentes. Só estou dizendo que classes parciais quase não fazem sentido naquele mundo, e seria estranho adicionar um padrão organizacional fundamentalmente novo que seja incompatível com ele. Se o mundo estivesse se movendo na direção oposta e todo mundo ficasse tipo "Scripts globais com efeitos colaterais no carregamento são incríveis, todos festejam prototype s uns dos outros", talvez nos sentiríamos diferente.

@RyanCavanaugh

Talvez ... Talvez eles não façam muito sentido ... Mas como eu disse inúmeras vezes, acho que mesmo neste segmento, as classes parciais têm como objetivo tornar a geração de código mais fácil ... Esse é o principal benefício que vejo deles vale o recurso em TS ... Eu, e eu temos que assumir muitos outros, mas isso é apenas um palpite, estou atualmente gerando toneladas de código TS do meu código C # ... Mas então a questão é como estender com segurança o código gerado Código TS ??? Esta é uma pergunta muito mais fácil de responder com classes parciais ... Atualmente estamos recorrendo a hacks para manter o código personalizado em nossos arquivos de código TS gerados ...

Obrigado

A mesma coisa aqui, geramos o "proxy" de texto digitado para nossas APIs C # e gostaríamos de poder estender esses objetos em texto digitado facilmente.

Não sou um especialista em JS, então devo estar faltando alguma coisa, porque não vejo por que ser capaz de dividir a declaração de classes em vários arquivos TS (com apenas uma declaração de classe em apenas um arquivo JS após a compilação) seria um problema para o mundo JS. A meu ver, é transparente para JS, como se tivesse sido escrito em um arquivo desde o início. Quer dizer, se você usar classes parciais ou não, a saída JS será exatamente a mesma, mas evitará o custo da extensibilidade do objeto JS para nossos desenvolvedores.

@RyanCavanaugh

Outra coisa que gostaria de mencionar que eu, pessoalmente, não estou pedindo ao compilador TS para resolver de alguma forma o dilema de empacotamento que pode surgir ao criar um módulo ES6 usando classes parciais em arquivos TS diferentes ... Faz todo o sentido para mim, pelo menos compilar vários arquivos TS em um único arquivo js para o módulo ES6 ... Mais uma vez, as classes parciais ajudam a geração de código significativamente ... Elas permitem que as ferramentas criem grandes partes do seu modelo com extensibilidade confiável ...

@kitsonk

Você continua pedindo um recurso de linguagem específico, como em "Eu adoraria um carro verde", mas não está chegando ao cerne da questão, por que o carro precisa ser verde? Se você explicou por que seu carro precisa ser verde, ou esteja aberto ao fato de que você realmente não se importa com a cor do seu carro, desde que atenda às suas necessidades reais.

Não, não estou pedindo mais um novo recurso. Tem sido afirmado repetidamente que a linguagem pode realizar o que eu quero. Forneci exemplos do que desejo (em C #), mas ainda não vi nos exemplos úteis uma maneira de chegar onde preciso chegar.

Não vejo por que você acha que é obrigação de Ryan lhe dizer como repintar seu carro, só porque, por motivos válidos, a montadora decidiu não produzir carros verdes.

Ryan parece ser um moderador aqui e está encerrando o assunto. É por isso. Se ele for especialista em idiomas, talvez não deva encerrar o problema antes de fazer um esforço para entender minha necessidade e dispensar essa solicitação sem fornecer um exemplo de como fazer isso com os recursos do idioma nativo. Eu declarei que gostaria de uma solução de tempo de compilação. E eu sou um ótimo desenvolvedor e já estou programando por um tempo em muitas linguagens diferentes e obscuras, e ainda tenho muita dificuldade com a sintaxe mixin. Não entendo como algo tão complexo e difícil de entender entrou na linguagem, e ainda assim todos estão rejeitando a elegância da sintaxe de classe parcial, simplesmente, ao que parece, porque foi emprestada do C #.

Ryan parece ser um moderador aqui e está encerrando o assunto. É por isso. Se ele for especialista em idiomas, talvez não deva encerrar o problema antes de fazer um esforço para entender minha necessidade

🤔

@RyanCavanaugh , sei que você se esforçou para ajudar. E, neste ponto, estou tentando experimentar os mixins. Ou apenas herança, ou talvez eu esteja com tanta pressa para terminar este projeto que estou esquecendo uma maneira de fazer isso com genéricos. Hmm...
Com herança e nomenclatura, posso fazer:

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 ) );
        }
    }
}

E isso funciona bem para mim. Não é tão bonito e força minhas classes geradas por código a terem nomes que não quero usar no código. Mas, novamente, funciona.

No momento eu sinto que os caras, que são fortemente contra aulas parciais, simplesmente interpretam mal o conceito de classes parciais, e eles pensam que classes parciais são algo que classes parciais não são.

Para deixar claro: classe parcial é uma construção sintática, que permite dividir a definição de uma mesma classe em vários arquivos físicos. As palavras-chave aqui são "iguais" e "únicas".

A documentação do TypeScript fornece uma definição incorreta de classes parciais (primeiro parágrafo aqui: https://www.typescriptlang.org/docs/handbook/mixins.html ).

Exemplo de conceito de classes parciais reais :

Arquivo "Point.generated.ts":

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

Arquivo "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);
  }
}

Eles são compilados em "Point.js" - e o nome vem do nome de uma classe, não dos nomes dos arquivos:

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;
}());

Mixins, quando usados ​​como "classes parciais" (e eu acredito fortemente que este termo na documentação é mal utilizado ), na verdade representam uma herança múltipla ( https://en.wikipedia.org/wiki/Multiple_inheritance ).

Portanto, como vemos, discutimos duas ferramentas diferentes: classes parciais reais e herança múltipla. Essas são duas ferramentas diferentes. E como qualquer ferramenta - eles têm sua própria área de aplicação. Em outras palavras: um carpinteiro pode usar um martelo em vez de uma marreta, quando a marreta não está disponível, mas o martelo não é a ferramenta certa.

IMHO, esta discussão não tem futuro e devemos pará-la. Embora eu ainda não veja problemas tecnológicos para aulas parciais reais , a equipe realmente não tem vontade de implementá-los, pelo menos por agora.

A equipe de TS não deseja implementar recursos específicos de TS, se possível. Se você quiser discutir mais, este é um lugar melhor: https://esdiscuss.org/topic/class-syntax-enhancements

Espero que haja um repositório GitHub no

@greendimka

No momento eu sinto que os caras, que são fortemente contra aulas parciais, simplesmente interpretam mal o conceito de classes parciais, e eles pensam que classes parciais são algo que classes parciais não são.

Para deixar claro: classe parcial é uma construção sintática, que permite dividir a definição de uma mesma classe em vários arquivos físicos. As palavras-chave aqui são "iguais" e "únicas".

Não acho que alguém aqui esteja entendendo mal a definição de classes parciais, acho que talvez você esteja entendendo mal a definição de classes em JavaScript.

Se você tem a impressão de que as classes JavaScript são como as classes encontradas em praticamente qualquer outra linguagem amplamente usada, então pode muito bem _parecer_ para você que os outros estão entendendo mal.

No entanto, como foi dito várias vezes nesta discussão, eu sei que disse explicitamente pelo menos uma vez, as classes JavaScript não são construções declarativas, elas são imperativas.

Eles não existem até que as instruções sejam executadas.

Defini-los depende de mutação.

Isso já os torna frágeis e dependentes da ordem quando se trata de herança.

Isso pode continuar e continuar ...

@cosmoKenney

Não entendo como algo tão complexo e difícil de entender entrou na linguagem, e ainda assim todos estão rejeitando a elegância da sintaxe de classe parcial, simplesmente, ao que parece, porque foi emprestada do C #.

Eu realmente não acho que isso esteja correto. Todo mundo adora C #, até mesmo os programadores Java 😛, e o TypeScript não é diferente por ser diferente.

@aluanhaddad

Acho que você talvez esteja entendendo mal a definição de classes em JavaScript ...

Para citado e abaixo: Estou falando sobre o TypeScript.
Como eu disse: não vejo nenhum problema técnico na implementação de classes parciais reais no TypeScript, que serão compiladas em construções de JavaScript.

@greendimka

Para citado e abaixo: Estou falando sobre o TypeScript.
Como eu disse: não vejo nenhum problema técnico na implementação de classes parciais reais no TypeScript, que serão compiladas em construções de JavaScript.

O problema com este satatement não existe uma classe TypeScript.

Oh garoto! Algumas pessoas usarão qualquer coisa como motivo.
Ok, não há classes no TypeScript. Ou classes TypeScript. Como queiras. Qualquer que seja.

Quero dizer que são classes JavaScript. Embora às vezes eu seja culpado de ser pedante, estou fazendo essa distinção muito especificamente porque precisamos concordar com a definição do termo classe antes de podermos discutir quais recursos são simples de adicionar a eles.

Acho que não queremos dizer a mesma coisa quando falamos de classes e isso causa dissonância cognitiva e impede o entendimento mútuo.

Acho que não queremos dizer a mesma coisa quando falamos de classes e isso causa dissonância cognitiva e impede a compreensão mútua.
Concordo plenamente!

Na verdade, a primeira postagem neste tópico descreve perfeitamente o que foi solicitado, portanto, essa discussão não tem futuro :)

@greendimka sim, não tem futuro, pois parece que o TypeScript nunca terá classes parciais. Na minha opinião, isso é bom.

No entanto, mal-entendidos e falhas de comunicação não são coisas boas, por isso ainda estou conversando com você.

Infelizmente, você não parece estar interessado em nenhum dos

A. Explicar o que são as classes do JavaScript permite que alguém mude de ideia e chegue a concordar com você que elas podem ser tornadas parcializáveis ​​trivialmente.

B. Aprender com os outros sobre o que são as classes de JavaScript, de modo que você deve entender o ponto de vista oposto.

Acho que é uma pena, mas não vou discutir isso mais se você quiser.

Não sou contra quaisquer discussões. Mas foi afirmado várias vezes aqui que classes parciais não serão implementadas (pelo menos não agora). Então eu acho que é melhor se concentrar em outras coisas. Talvez o TypeScript possa ser alterado no futuro, quem sabe.
Em relação a A e B. Eu odeio JavaScript. Mas eu o conheço muito bem e, claro, sei como as classes são "representadas" nele. Mas o objetivo de toda essa discussão não era modificar o JavaScript, mas melhorar as habilidades do TypeScript, como uma linguagem superior, que produz código JavaScript de "baixo nível".
Imagine que substituiríamos TypeScript por C ++ e JavaScript por código de máquina. O código de máquina carece da maioria dos conceitos existentes em C ++. Mas o C ++ deve parar de evoluir por causa disso? Claro que não. Deve parar de evoluir porque alguns compiladores antigos não serão capazes de compilar? Claro que não - você dá um novo recurso aos desenvolvedores e diz a eles: funciona com novos compiladores - você (desenvolvedores) decide se deseja usá-lo.

@aluanhaddad
Cara, oh cara. Quem se importa com a parte Javascript da equação? TypeScript é uma camada acima, ou como você quiser formá-la. Lembra das antigas discussões 4GL vs. 3GL? TypeScript está para Javascript assim como 4GL está para 3GL, mais ou menos. Além disso, o argumento de que TypeScript é ES6 com tipos fortes, portanto, Partial Classes está fora do escopo do roteiro do TypeScript é LAME. Temos Mixins, Generics, Modules, Name Spaces e Type Casting. Então, por que não ir mais longe com as aulas parciais?

Tudo o que queremos das classes parciais é o açúcar sintático que nos permite amalgamar todas as várias definições de uma única classe TypeScript em uma definição de classe final - nível baixo - 3GL - Javascript. Não deve haver impacto na definição final da classe JavaScript, certo? Por que o produto final da transpilação para Javascript faz parte dessa discussão? Seriamente.

@cosmoKenney "é apenas ES.next with types" é um grande argumento de venda que converte desenvolvedores de JavaScript. Se você quiser algo mais, está procurando no idioma errado. Talvez tente Scala.js em vez disso?

editar: Acabei de ter uma percepção interessante de que no ES.next classes parciais podem ser implementadas com um carregador de módulo personalizado. Se você usar

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

o carregador pode mesclar todas as definições exportadas de MyClass de qualquer arquivo que corresponda ao curinga em uma única classe e, em seguida, fornecer isso.

@spion Eu gosto de como você está se referindo a eles como classes parciais ES.next. Um carregador de módulo ES, como um navegador, ou carregador polyfill, como SystemJS, ofereceria suporte a classes parciais.
Um dos principais problemas em adicionar esse recurso no nível do TypeScript é que ele quebra as ferramentas existentes, como carregadores e empacotadores. Por outro lado, se o ECMAScript os especificasse, todas essas ferramentas implementariam o recurso e o TypeScript permaneceria compatível com todas essas ferramentas.

Eu definitivamente concordo com você em relação a Scala.js

@cosmoKenney

Temos Mixins, Generics, Modules, Name Spaces e Type Casting. Então, por que não ir mais longe com as aulas parciais?

Mixins como vistos no TypeScript são um padrão de design ECMAScript que _types_ do TypeScript.

Os genéricos são um recurso do sistema de tipos, portanto, não se aplicam.

Módulos são um recurso ECMAScript.

Os namespaces são um açúcar sintático e uma formalização de um padrão de design ECMAScript.

O Type Casting não existe no TypeScript.

@aluanhaddad por que você continua fazendo isso como uma coisa de tempo de execução? Carregadores de módulo e tudo o que não tem nada a ver com transpilar.

+1

@cosmoKenney

@aluanhaddad por que você continua fazendo isso como uma coisa de tempo de execução? Carregadores de módulo e tudo o que não tem nada a ver com transpilar.

Isso não está correto.

Olhe para os ecossistemas SystemJS e Webpack e você verá o contrário.

Ainda mais tradicionais, e fluxos de trabalho gulp sólidos dependem da correspondência entre os arquivos de entrada e saída.

+1

Eu preciso de uma classe parcial porque estamos gerando a maior parte da classe base usando ferramentas (para automatizar quando houver mudanças no modelo). Em seguida, usamos a classe parcial para adicionar funcionalidade à classe em arquivos separados para que ela não seja sobrescrita pela ferramenta (que gera a classe automaticamente).

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

remojansen picture remojansen  ·  3Comentários

manekinekko picture manekinekko  ·  3Comentários

weswigham picture weswigham  ·  3Comentários

MartynasZilinskas picture MartynasZilinskas  ·  3Comentários

wmaurer picture wmaurer  ·  3Comentários