Typescript: Частичные занятия

Созданный на 29 авг. 2014  ·  224Комментарии  ·  Источник: microsoft/TypeScript

Добавить поддержку частичных классов. Mixins - это не то же самое, потому что это реализация во время выполнения. Требуется реализация компиляции, при которой частичные классы будут объединены в один перед преобразованием машинописного текста в 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!"; }
}

будет:

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

Самый полезный комментарий

Вариант использования частичных классов - сгенерированные прокси-классы. Например, обертки WebAPI или SignalR. Было бы действительно неплохо иметь возможность расширять сгенерированные прокси-классы с помощью настраиваемой логики. Особенно при создании классов для моделей было бы неплохо иметь возможность прикреплять бизнес-логику непосредственно к классам модели, возвращаемым из api.

Все 224 Комментарий

Я думаю, что вы также используете «частичное» для модулей и помогаете решить эту проблему, поднятую здесь.

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

@disshishkov возможно будет достаточно методов расширения: # 9

В каких ситуациях вы хотите использовать это и как существующие решения этого не делают?

Обратите внимание, что это было реализовано в C # для поддержки сценариев редактирования типа WinForms, где у вас есть автоматически сгенерированный файл и редактируемый пользователем файл, относящиеся к одному и тому же типу; Я не уверен, что такие ситуации применимы в JavaScript / TypeScript.

Некоторые классы могут иметь много номеров строк, просто для лучшего чтения поможет разделение на несколько отдельных классов. Вы можете разделить по разным типам, например, по логике (в случае, если эту логику нельзя переместить в другой класс), по видимости (частный и общедоступный) и т. Д. В случае, если при расчесывании частичных классов могут возникнуть проблемы (например, 2 частичных класса совпадают с одним и тем же объявлением метода / переменной), компилятор должен уведомить и выдать ошибку.

Это не очень убедительно. Если ваш класс настолько велик, что его нельзя удобно редактировать в одном файле из-за его огромного размера, это очень серьезный запах дизайна (например, комментарии в http://programmers.stackexchange.com/questions/157482). Разделение по разным типам / логике / видимости / и т. Д. - это то, что может обрабатываться IDE или организацией в одном файле.

Стоит обсудить, почему ваши классы такие большие, что по ним невозможно перемещаться (это потому, что инструменты навигации должны быть лучше?), И есть ли лучшие способы, которыми язык мог бы поддерживать _разложение_ класса, а не просто _ разбиение_ на него.

Лично я считаю, что «частичные классы» должны существовать, но вести себя как модули, которые сливаются вместе. У меня есть система с модулями, которая, хотя intellisense видит все модули (как и должно), фактический JS, содержащий его, загружается только при необходимости. Я думаю, что то же самое было бы хорошо и для классов - загружать только необходимые части. Я также хотел создать функцию и использовать класс для ее дальнейшего расширения. В настоящее время это можно делать только с модулями.

Вариант использования частичных классов - сгенерированные прокси-классы. Например, обертки WebAPI или SignalR. Было бы действительно неплохо иметь возможность расширять сгенерированные прокси-классы с помощью настраиваемой логики. Особенно при создании классов для моделей было бы неплохо иметь возможность прикреплять бизнес-логику непосредственно к классам модели, возвращаемым из api.

+1 квантеторе.

Вариант использования точно такой же, как у .net; генерируется часть класса (в нашем случае из схем Avro), и вы хотите добавить дополнительный вспомогательный код для работы со сгенерированными классами.

Мне нравится это предложение. Я хотел бы иметь частичные классы, чтобы отделить атрибуты / свойства классов, образующих мою «иерархию данных», которые могут быть собраны в одном файле, от их методов, которые могут быть разделены в нескольких других файлах. Это сделало бы код более ясным и легким для понимания с одного взгляда, imo.

Мой файл иерархии данных :

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

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

Файл, содержащий методы класса A :

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

Файл, содержащий методы класса 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 от меня. Я хотел бы использовать шаблоны tt для создания классов Typescript, но добавить к ним в отдельный файл, который не будет заменен генерацией шаблона.

+1 Это действительно поможет мне с моими автоматически сгенерированными классами, которые мне нужно расширить

Частичный класс действительно хорош, но иметь его не имеет смысла. Это показало, что у вас есть проблема с дизайном. Эмпирическое правило в программировании состоит в том, чтобы держать его простым и глупым (KISS), независимо от того, какие языки вы используете, - это разбивать большой класс на более мелкие - либо 1) разбивая скрипты на классы и вызывая их (другие классы также могут называть это меньшие классы тоже вместо того, чтобы заново изобретать колесо), или 2) разбить на части и преобразовать в Abstract / Interface / Inheritance / Virtual и т. д. или полиморфизм.

Допустим, у вас есть Транспортное средство, в котором есть все необходимое. Частичный класс не имеет смысла и привносит сложность и накладные расходы здесь, где это имеет больше смысла. Создайте класс Engine, класс Tranny, класс Drivetrain, класс Door и класс Tire как отдельные классы и переместите в них скрипты, которые при вызове могут определять автомобиль в классе Vehicle. Это уменьшает объемные сценарии и сложность сценариев. Скорее всего, вы обнаружите, что здесь и там есть скрипты Door, которые можно упростить с помощью класса Door. Интерфейс / Аннотация / Наследование / Виртуальный можно использовать для изменения определения класса Door в некоторых скриптах в классе Vehicle.

Таким образом, у вас также будет меньше времени на разработку. Когда-то у меня был блог ASP.NET C #, в котором используется множество частичных классов. Я боролся с этим из-за слишком большого количества частичных классов, и вы не знаете, где они. Это похоже на работу с логикой GOTO в программировании. Плохо плохо !! Мне так и не удалось успешно создать патч для исправления ошибки, и я не смог настроить скрипт, потому что он был слишком похоронен в куче частичных классов (как и некоторые переименованные формулировки).

Просто говорю это как мысль в 1 цент из моей головы.

@ fletchsod-developer: некоторые ситуации правильно обрабатываются посредством наследования, но не все. Некоторые разработчики могут неправильно использовать частичные классы, но не все. В некоторых ситуациях я считаю частичные классы очень и очень полезными, но если они вам не нравятся, вам не нужно их использовать.

@ jfrank14 , как было предложено выше @basarat , # 9 не сработает для вас?

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

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

@NoelAbrahams , могли бы вы получить доступ к определениям из файла1 в файле, но не наоборот? Для меня это нормально, если не зависит от порядка включения.

Я действительно надеюсь, что вы, ребята, рассматриваете частичные классы а-ля C # для TS ... для меня единственная причина - это генерация кода, которая является основной причиной частичных классов C # ... в настоящее время мы делаем много генерации кода TS, и мы ожидаем, что будем делать все больше и больше ... в настоящее время мы должны полагаться на "//

+1 - действительно можно использовать это для добавления функциональности в автоматически сгенерированные классы; не существует другого элегантного решения, о котором я мог бы подумать или о котором читал на сегодняшний день ...

+1 - классы, генерируемые кодом. Объекты C # / Java, сериализованные для клиента

Это нужно также для слияния сгенерированных классов!

Это была бы очень полезная функция, позволяющая просто объединить сгенерированный код с несгенерированным кодом.

+1

+1 - Он нам также нужен для частично сгенерированных классов, было бы более элегантно, чем наследование, которое испускает довольно много кода, который для этой цели не нужен.

+1

Снова столкнитесь с этим ограничением - пожалуйста, добавьте эту функцию! : +1:

+1

+1

+1

+1

Люди случайным образом +1 не собираются двигаться дальше.

На данный момент варианты использования:

  • У меня действительно большие классы, охватывающие несколько файлов. @RyanCavanaugh указал, что это, вероятно, серьезная проблема дизайна с кодом, который настолько сложен и недостаточен, чтобы гарантировать сложность реализации.
  • Это им нравится.
  • Другой аргумент в том, что они есть в C #.
  • Другой аргумент касается сгенерированного кода на других языках.

Второй и третий варианты использования не являются убедительными, а четвертый, я подозреваю, требует некоторого уровня объяснения того, как доставка №9 не будет соответствовать очень похожему требованию и даже декораторам (№ 2249).

Похоже, что №9 будет делать то же самое и будет даже более общим (потому что вы можете расширять классы, кроме своего собственного). Но было бы разумно захотеть, чтобы содержимое класса было в двух файлах (возможно, потому, что один написан от руки, а другой сгенерирован шаблоном), и сказать, что оба файла определяют основные члены класса с равной важностью и не требуют этого. файл "расширяет" определение класса в другом.

Это почти экзистенциальная разница, но, возможно, важна для некоторых людей в некоторых ситуациях.

Я не хочу частично использовать классы по указанным причинам, но по модулям это имеет смысл, ИМХО.

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

Частичные классы чрезвычайно полезны для специализированных вариантов использования. Это не нарушение какого-либо шаблона ООП, а способ упорядочения документа кода, который обеспечивает:

  1. Возможность разделить определение класса / типа на несколько файлов. Для огромных файлов кода это функция ремонтопригодности ++.
  2. Возможность компилятора / системы сборки использовать условную компоновку во время компиляции. Аналогия с MSBuild, конкретный вариант использования в C #: DBML, классы конструктора и даже более модные вещи, такие как https://github.com/dotnet/corefx/pull/2045/files.
  3. Встроенная защита, поэтому два (или более) разработчика не сталкиваются друг с другом во время проверки кода. Удары по разрешению конфликтов! :)
  4. Позвольте вам сгенерировать код в основном частичном классе, а затем создать еще один частичный класс для пользовательских перегрузок. Прекрасно работает!

Я верю в разделение бизнес-логики и уровней данных на отдельные классы, и я в основном использую статические методы на этих уровнях. Я действительно не понимаю, как это могло быть больше кода и менее удобным в обслуживании.

В то время как метод расширения - это функция основного языка, на которую влияют OO-шаблоны и будут обрабатываться во время компиляции, частичные классы имеют дело с макетом документа / кода, который должен быть сшит на этапе предварительной компиляции. Несмотря на сходство частичных классов, абстрактных классов и методов расширения, существует четкое различие.

: +1: за привнесение безобидных "частичных классов" в TypeScript.

Я использую частичные классы для генерации кода - моя команда должна иметь возможность автоматически генерировать часть класса в одном файле и вручную добавлять пользовательские методы и свойства в другой файл. Я немного сбит с толку, почему @RyanCavanaugh «не уверен, что такие ситуации применимы в JavaScript / TypeScript», поскольку этот вариант использования не связан с выбранным языком.

Ни декораторы, ни методы расширения не решают проблему генерации кода так элегантно, как частичные классы, и каждый из них имеет существенные дополнительные сложности.

+1

+1

+1 для целей генерации кода.

В среде C # интеграция транспилятора классов C # в классы TypeScript устранит необходимость хранить два разных определения классов между сервером и клиентом, поскольку клиентская сторона, также известная как класс TypeScript, будет автоматически сгенерирована, а частичный класс то, что эта проблема запрашивает, позволит настраивать при необходимости, все еще разрабатывая собственный TypeScript.

+1 для сценариев генерации кода

+1 чрезвычайно полезен для генерации кода

Почему бы не использовать наследование для генерации кода?

Наследование более беспорядочно и связано с большими накладными расходами.

В пн, 10 августа 2015 г., 10:09 Горгий Косев [email protected] написал:

Почему бы не использовать наследование для генерации кода?

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129468291
.

Наследование создает бесполезные связи имен

Наследование менее круто, потому что оно:

  1. Без нужды углубляет цепочку прототипов и замедляет создание экземпляров,
    хотя это не имеет большого значения для большинства приложений, скорее всего, для всех, кроме игр;
  2. Из-за отсутствия абстракции и переопределения либо теряется безопасность типов, либо
    вводит необходимость в некоторых неприятных паттернах, таких как литье между двумя
    типы;
  3. В целом сбивает с толку, поскольку наличие базового класса передает идею, что
    он может быть расширен другими классами, что редко случается с генерацией кода.

Я не говорю, что машинописный текст ДОЛЖЕН иметь частичные классы для поддержки генерации кода, только
что частичные классы намного лучше, чем наследование или
состав.

@JoshMcCullough Накладные расходы на наследование в JS минимальны, и я не понимаю, насколько это беспорядочно, чем частичные классы: с ними вы потенциально можете разбить класс на 10 файлов и преследовать свои методы повсюду.

@ yahiko00 О каком "бесполезном связывании имен" мы говорим? Ссылаетесь ли вы на то, что вам нужно сделать, чтобы получить номер 2 ниже (жесткое кодирование имени расширенного класса)?

@hdachev

  1. Есть некоторые накладные расходы, но они довольно минимальны - они заметны, только если вы делаете что-то, что напрямую преобразуется в пару машинных инструкций.
  2. Хороший момент - вы не получаете расширенный класс, когда вам нужно ссылаться на сгенерированный класс из других сгенерированных классов.
  3. Эти классы действительно предназначены для расширения - иначе вы могли бы сгенерировать их и покончить с этим, верно?

AFAIK это не на радаре ES7 +, не так ли? Это может быть проблематично ...

В любом случае присоединение дополнительных вещей к прототипу существующего класса довольно тривиально в JavaScript и вовсе не редкость, поэтому было бы неплохо иметь синтаксис для этого ...

В TS это просто, но JS за кулисами удвоит
"прототипирование", если вы использовали базовые классы для всех ваших сгенерированных данных
объекты. Вероятно, это не серьезно повлияет на производительность, если у вас нет тонны
объекты данных, но в этом нет необходимости, когда мы знаем, что "частичный
занятия »возможны.

В пн, 10 августа 2015 г., в 11:03 Горгий Косев [email protected]
написал:

@JoshMcCullough https://github.com/JoshMcCullough Накладные расходы на наследование
в JS минимален, и я не понимаю, насколько он более беспорядочный, чем частичные классы:
с ними вы потенциально можете разделить класс на 10 файлов и преследовать свои
методы повсюду.

@ yahiko00 https://github.com/yahiko00 Что за "бесполезное имя"
связь ", о чем мы говорим? Вы говорите о том, что вам нужно
что делать, чтобы получить № 2 ниже (жестко указать имя расширенного класса)?

class MyClass расширяет GeneratedMyClass {...}

@hdachev https://github.com/hdachev

1.

Есть некоторые накладные расходы, но они минимальны
https://jsperf.com/prototype-chain-vs-direct-calls - только заметно
если вы делаете что-то, что переводится прямо на пару машин
инструкции
2.

Хороший момент - вы не получаете расширенный класс, когда вам нужно
ссылаться на сгенерированный класс из других сгенерированных классов.
3.

Эти классы действительно предназначены для расширения - в противном случае вы могли бы
сгенерируйте их и покончите с этим, не так ли?

AFAIK это не на радаре ES7 +, не так ли?

В любом случае, добавление лишних вещей к прототипу существующего класса
довольно тривиален в JavaScript и вовсе не редкость, поэтому может быть
хорошая идея иметь синтаксис для этого ...

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/563#issuecomment -129483174
.

@ Davidhanson90, в этом файле нет классов. Вы хотели опубликовать это в другой теме?

Я опубликую в правильной теме. https://github.com/Microsoft/TypeScript/issues/447

+1

+1

+100

+1

+1

+1 для генерации кода.
Я занимаюсь angularjs и webapi, и я хочу создать инструмент, который по сути создает автоматические определения JS из моих объектов и служб C #. После этого я хочу иметь возможность расширять эти классы без необходимости редактировать «скаффолдинговые» определения JS. Я видел, что об этом просят еще несколько человек, и похоже, что у нас есть аналогичные варианты использования.

+1 для генерации кода

+1 для генерации кода, angular имеет много шаблонного кода, мы уже делаем много его генерации, но с частичными классами мы могли бы сделать гораздо больше.

+1 еще раз для генерации кода. Попытка расширить классы в другом файле в лучшем случае беспорядочная.

@RyanCavanaugh , что нужно, чтобы выйти из режима «+1» и двигаться в продуктивном направлении?

-1 для миксинов лучше использовать следующий синтаксис: # 2919

Не уверен, почему у нас не может быть и частичных классов, и миксинов; две совершенно не связанные между собой функции.

+1

Было бы неплохо, когда код сгенерирован, но вы не хотите изменять его, чтобы добавить что-то в сгенерированный код.

+1

У меня есть предложение, которое должно удовлетворять обоим из этих вариантов использования:

  1. Вы хотите писать методы своего собственного класса в разных местах.
  2. Вы хотите добавить новые методы в чужой класс, например, встроенный.

Для (2) обычно вы просто используете оператор :: чтобы получить инфиксный синтаксис.

Но если вы хотите, чтобы старый класс был частью нового интерфейса, или если вы хотите динамически отправлять, вам нужно будет фактически изменить прототип.

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

(В этом примере я использую имена строковых методов, но поскольку это встроенные типы, использование символов будет лучше, если их можно будет проверить по типу.)

Преимущество этого метода по сравнению с истинными частичными классами заключается в том, что класс имеет одно определение, а другие места просто расширяют его. Это означает, что есть четкое место для импорта класса и упрощается перевод в JS.

: +1: Я тоже хотел бы видеть неполные классы.

Каков статус этой функции?

Этот вариант использования, например:

У меня есть пакет Math , который определяет класс Set с методами Set#add , 'Set # remove'

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

У меня есть необязательный тяжелый пакет Relational , который определяет классы Relation и Tuple .

Этот пакет Relational также добавит метод Set#product .

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

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

Частичные классы позволили бы мне более гибко составлять классы. Если я не хочу использовать тяжелый пакет relational , я все равно могу использовать базовую функциональность Set. Другие классы partial войдут только и добавят классу дополнительную функциональность.

Я ломаю голову, чтобы сделать что-то, что в этом случае будет иметь хороший API без использования частичных файлов.

Все, что я мог предложить, - это какой-нибудь шаблон репозитория.

// 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 Я использую t4 для генерации классов и прокси через WebApi. Я думаю, что автогенерация широко используется в машинописном тексте, и когда дело доходит до автогенерации, необходимы частичные классы!

Было бы здорово разделить аспекты одного и того же класса, особенно в React.
+1

+1 Генерация кода и «Достаточное разделение» в случае чистых многократно используемых объектов данных с атрибутами, зависящими от клиента, для подобной проверки, которую было бы неплохо определить в классе, но поддерживать в отдельном фрагменте исходного кода.

+1 для генерации кода частичных классов и ручного кодирования дополнительных членов для этих классов в отдельных файлах.

+1

+1 У меня большая база методов в нашем js api сервисе, лучше всего их разбить на файлы, чтобы было легче понять каждый метод.

api, например fb (facebook) или gq (google analytics) , где вам предоставляется один глобальный класс или объект, который вы можете использовать в процессе разработки.

+1

Эта функция была бы большим плюсом и для нас.

Мы разработали промежуточное программное обеспечение, с помощью которого все типы клиентов могут подключаться и общаться с нашими серверами через него.

Мы генерируем часть клиентского кода на основе того, что предоставляется серверами.

Пока все было нормально. Но теперь мы хотели бы иметь возможность передавать разнородные коллекции объектов (но с одним и тем же базовым классом). Переданные объекты являются частью кода, созданного на основе серверного API.

В этом случае ключом к использованию возможностей наследования являются абстрактные методы. Но наши разработчики не будут добавлять методы в сгенерированный код, я полагаю, все знают почему;)

Насколько я понял (я разработчик C #), предлагаемое здесь решение (https://github.com/Microsoft/TypeScript/issues/9) не позволит нам этого сделать.

Итак, частичные классы нам бы идеально подошли. Мы могли бы сгенерировать класс как частичный, и если разработчикам потребуется добавить логику или методы, им просто нужно будет написать другую часть, где они захотят.

+1

+1 очень полезно для генерации кода

Интересно, устраняет ли расширение модуля необходимость в этой особой функции.

Если у вас есть сгенерированный класс, вы сможете сделать что-то вроде ...

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 Похоже, это хороший способ реализовать это под прикрытием, но мне все равно нужна надлежащая первоклассная поддержка на языке без необходимости вручную изменять прототипы и поддерживать этот интерфейс.

Я согласен с Elephant-Vessel. Созданная вручную часть частичного класса должна быть настолько слабо связана (во время разработки в TS), насколько это возможно, с сгенерированной частью.

Предложение @ david-driscoll работает во многих случаях. Однако, насколько я могу судить, прототипы не имеют доступа к внутренним переменным. Это ограничивает полезность этого подхода в контекстах генерации кода, когда вы хотите сгенерировать основные части своих классов, а затем дополнить их пользовательским кодом.

+1

@ david-driscoll, который выглядит как отличный способ избавиться от сахара в этой функции. (необходимо добавить возможность доступа к частным / защищенным свойствам). Интересно, что произойдет с объявлением нескольких частичных классов? Какова будет их взаимная видимость?

++ 1 Отсутствие частичного действительно причиняет мне боль, когда я пытаюсь расширить мои автоматически сгенерированные модели.

Я думаю, что единственное, что здесь имеется в виду, - это в основном то, что предложил @david-driscoll - вы можете объявить новые методы (которые будут помещены в прототип) и новые свойства _non-initialized_ (которые не имеют кодогенерации), но не новые инициализированные свойства (потому что они имеют побочные эффекты для кодогенератора конструктора).

отвергнуты, извините за мой французский, частичные классы - это глупая попытка разбить ваши классы богов, которые неизбежны в ООП, на несколько файлов, чтобы ими было легче управлять (хотя они все еще являются классами богов)

с ООП у вас нет будущего (были там, знаете, о чем я говорю)

приходите к FP, у нас есть куки и способ написать программу без единого класса и дерьмо вроде этого

Таким образом, мы уже в принципе разрешаем это в любом случае через interface A { method(): void } A.prototype.method = function() { }; ; кодифицируя это в сахар, именно это имеет смысл.

Одна вещь для bikeshed - ключевое слово partial . Краткая историческая виньетка: в C # partial class изначально должен был быть extension class (который вы помещали во все объявления, кроме одного), но не было четкого различия в том, какие объявления будут extension и какое объявление будет объявлением без расширения. Вместо этого у вас есть модификатор partial который вы должны разместить на _все_ классах.

Это _не_ дело TypeScript; скорее наоборот. Здесь только один класс (назовем его «объявление _primary_») может иметь конструкторы / инициализированные поля-члены; первичное объявление не получает никакого нового модификатора. Любое другое объявление (назовем их «объявления _extension_») может иметь только статику, методы и неинициализированные поля, и вам _ нужно_ иметь в них какой-то модификатор, чтобы указать, что они не являются первичными.

На данный момент наилучшее предположение

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

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

: блестки:: байк:: дом:: блестки:

Байкшеддинг или нет, но это звучит ужасно, как # 311, от которого отказались из-за вероятного вмешательства в будущие стандарты ECMAScript. Почему это стоит рассмотреть, а правильная поддержка миксинов - нет?

Использование ключевого слова extension конфликтует с методами расширения ? Или это полностью отрицает методы расширения?

@kitsonk Я думаю, что в предложении по

@ Elephant-Vessel Я не думаю, что методы расширения применяются, если возникают частичные классы. Классы - единственное место, где методы расширения имеют смысл с учетом наших ограничений, а предлагаемый оператор связывания ES7 + - лучший способ использовать синтаксис, подобный расширению.

@RyanCavanaugh : мне кажется немного ограничивающим требовать, чтобы один класс был основным, а другие - нет. Если вы хотите разделить свой код так, чтобы один файл содержал все методы класса, а другой - все свойства, ни один из них не кажется более очевидным, и почему вы должны быть вынуждены выбирать? Разве не чище иметь частичные компоненты, в которых все они равны по важности и объединены компилятором?

@RyanCavanaugh ... Я бы сделал это точно так же, как это делает С # ... он отлично работает для генерации кода ... что, я считаю, было основной проблемой, которую функция предназначена для решения в С # ... хотя моя память нечеткая ... никогда не было проблем, он существует уже много лет и прошел боевые испытания ...

Если частичные / расширенные классы не смогут иметь инициализированные поля, я думаю, было бы очень удобно поддерживать что-то вроде частичных методов, которые мы знаем из C # , в основном легкий контракт, специфичный для частичных классов. Так нравится:

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

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

Необходимость частичной / расширенной сторонних классов может быть довольно легко (?) Решена, если сделать partial/extends как правило, необязательным, но необходимым для использования частичных методов.

Я думаю, что это могло бы придать некоторую выразительную и действенную силу концепции, которую мы здесь хотим.

Тем, кто ищет миксины.

Предположим, что объект инициализирует предложение, с помощью которого (и анализ потока) миксины могут быть выполнены элегантно, естественно, идиоматически, насколько это возможно в JavaScript:

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

Такие языки, как Java, не предлагают частичных классов / структур. Это нишевая особенность C #. Имитация концепций "частичный класс / структура" C # в полной мере - лучший способ здесь.
На этапе предварительной компиляции первое, что может сделать компилятор TS, - это сшить частичные блоки кода в памяти, а затем перейти к обычному маршруту конвейера компиляции. Этого будет более чем достаточно, чтобы претендовать на v1.0.0-preview1.0000 функции частичных классов в TypeScript.
Все, что выходит за рамки того, что предлагает C #, может быть введено в более поздних версиях, когда функция будет развиваться и переживет пробную / предварительную версию. Угловые случаи и подобные непонятные вещи можно обсуждать отдельно по одному, и мы можем позаботиться о подгонке и отделке позже ... ИМО.

Я также согласен с тем, чтобы использовать как частичные, так и расширенные классы точно так же, как в C #
это лучший способ с точки зрения синтаксиса и семантики, оба работают
очень хорошо для их соответствующих случаев использования. Частичные занятия улучшают
удобство использования сгенерированного кода. Методы расширения улучшают удобство использования
статических методов, которые работают с интерфейсами, указанными дженериками и третьими
типы вечеринок. Оба действительно классные, но помните, что они не
та же функция, я говорю это здесь из-за предложенного выше синтаксиса.

Методы расширения позволили бы это сделать (извините за отсутствие форматирования,
набирать текст на телефоне):

расширить MyType [] {
somethingSpecificToMyType () {...}
}

расширить WeirdThirdPartyDatastructure{
то же самое() { ... }
}

Это, помимо прочего, например, возможность использовать библиотеки, такие как подчеркивание
без мусора, что в основном означает использование синтаксических преимуществ
расширение встроенных типов без их фактического загрязнения.

Так кратко - давайте не будем относиться к ним как к одному и тому же, они оба очень
круто но не то же самое. Мне лично хотелось бы иметь и то, и другое в нашем
язык.

Кстати, в качестве примечания, я полагаю, что многие люди съеживаются от идеи
кодогенерации и готовы всем объяснить, что в нем нет места
в javascript, потому что есть больше идиоматических способов добиться того же.
Есть два способа, которыми генерация кода очень помогает в некоторых
проекты, такие как тот, над которым я сейчас работаю.

  • В проектах, чувствительных к производительности, таких как игры, где вы просто делаете
    все, чтобы максимально использовать бюджет вашего процессора с помощью полезной работы, codegen помогает тонне
    для генерации кода, который одновременно прост в рассуждении и хорошо оптимизируется
    / мономорфный код, который очень хорошо подстраивается. Например, в
    игровой движок сущностей / компонентов, сгенерированный код может помочь с множеством вещей
    например, очень быстрый шаблонный код для склеивания компонентов, событие
    отправка и т. д.
  • В приложениях для крупного бизнеса сгенерированный код помогает максимально эффективно использовать
    система типов, например, путем выдачи методов с очень специфическими
    подписи для запросов к базе данных и т. д., что увеличивает время компиляции
    проверяет, помогает с рефакторингом, а также значительно упрощает отладку
    потому что он позволяет выполнять очень простой код оркестровки.
  • Самый большой - это когда вы синхронизируете какой-то компонент между
    несколько языковых сред, например, что-то связанное с обменом сообщениями
    например, буферы протокола или просто интерфейсы сообщений json и т. д.

В качестве еще одного бокового узла для тех, кому интересно, какой частичный
классы связаны с генерацией кода, значение сохранения сгенерированных частей
класса в отдельный файл исходит из требований системы управления версиями -
сгенерированный материал обычно представляет собой быстро меняющийся артефакт сборки, и вы
не хочу, чтобы это было зафиксировано в вашем репо.

Мне нравится идея реализовать это в соответствии с хорошо известной и испытанной моделью этой концепции, как и в C #. «Подражание» имеет некоторые негативные коннотации, но оно имеет значительную ценность, если сделано с умом. С другой стороны, «интеллектуальная» часть этого состоит в том, чтобы знать о различных обстоятельствах и ограничениях, поэтому я не буду возражать, если частичные файлы для TypeScript не совсем похожи на частичные файлы для C #.

@ Elephant-Vessel, я согласен с тем, что это не обязательно должна быть копия частичных файлов C #, общий дизайн может быть сначала разработан на основе вкуса TypeScript / JavaScript, максимально вдохновляясь частичными элементами C #. Мое предложение состоит в том, чтобы в качестве первого шага использовать 'регистрацию partial keyword' и 'сшивание частичных файлов' и отправлять их в качестве экспериментальной функции / функции предварительного просмотра (чтобы потребитель не запускал в зависимости от нее в производственном коде правильно с места в карьер). Позже, основываясь на реакции сообщества, дорабатывайте эту функцию до тех пор, пока она не будет готова к выпуску и не будет RTM-версия. Если мы заранее будем беспокоиться о всевозможных хитростях и сценариях, то, скорее всего, это еще больше задержит дело.

+1 - Для расширения и поддержания сгенерированных объектов.

+1 за поддержание сгенерированного кода

Я создал полифил и разделил свой большой класс.

Определите класс как интерфейс.

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

Реализуйте членов класса.

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

Реализации слияния.

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

Я думаю, что это ужасная идея по одной конкретной причине: она значительно ухудшит и без того сложные правила для глобальных, внешних, внешних, пространственных имен и хрупких проблем наследования, зависящих от порядка объявления. Это не C #, и разрешение имен и объявления членов сильно отличаются.

Возможно, используйте объект или пространство имен вместо класса, как шаблон модуля, если вам действительно нужно это сделать. В противном случае ваш класс, вероятно, слишком велик.

Также декораторы могут использоваться для реализации ассоциаций между сгенерированным кодом и написанным вручную кодом.

У вас есть другие решения?

Я бы тоже хотел, чтобы это добавили в TS

+1 для целей генерации кода (DTO из справочника службы WCF с дополнительными определениями частичных классов)

+1 для целей генерации кода. Мой вариант использования: у меня есть компоненты React, чьи функции render() генерируются извне с помощью шаблонов реакции .

С помощью классов partial я мог проверять типы таких функций по сравнению с основным классом ( Component ). Без частичных классов единственное, что я могу сделать, - это привязать функцию к основному классу и надеяться на лучшее.

В следующей версии TS 2.0 я подумал, что некоторые случаи генерации кода могут быть охвачены функцией типа this .

Например, в случае, который я описал ранее, вместо

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

я могу написать

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

+1 Как разработчик, мне нужны частичные классы, чтобы несколько шаблонов могли генерировать / изменять один и тот же класс, распределенный по нескольким файлам.

Мои комментарии были конкретно в отношении частичных классов, а не миксинов. Эти два принципиально разные. Частичные классы предназначены для разбиения физического кода, миксины - о разбиении логического поведения на многоразовые черты, обогащающие другие объекты как на уровне типа, так и на уровне значения.
Я не думаю, что частичные классы - хорошая идея для TypeScript. С другой стороны, миксины - хорошая идея, обеспечивающая повышенную выразительность и очень хорошо сочетающаяся как с системой типов TypeScript, так и с идиомами JavaScript, как указывает @ aleksey-bykov

@wendellm, можете ли вы придумать чистый способ сделать это, учитывая, что модули ES являются физическими. Частичные классы хорошо работают и имеют большой смысл на таком языке, как C #, где модули являются логическими, а не физическими. С точки зрения CLR пространств имен не существует, просто в именах классов могут быть точки (источник - интервью с @ahejlsberg)

+1 Мне нужен частичный класс!

мы можем расширить интерфейс? Интерфейс также может иметь некоторые реализованные функции, такие как Swift:

interface Rect {
    x: number
    y: number
}

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

Быстрая версия:

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

+1

Генерация кода и частичные классы идут рука об руку.

Наследование - плохое решение того, что по сути является прославленной директивой #include .

Возьмем, к примеру, Angular 2. Он не будет читать метаданные из родительского класса, что делает непрактичным расширение по наследованию.

@tsvetomir , это проблема с Angular, а не с TypeScript.

Наследование - плохое решение того, что по сути является прославленной директивой #include.

Да, наследование - плохое решение, но проблема в первую очередь в использовании классов. У них есть свое место, но оно очень ограничено. Классы JavaScript довольно слабые и невыразительные по сравнению с классами на других языках.

Машинопись не является "угловым" языком ... Никогда не был

23 июля 2016 г. в 21:46 Алуан Хаддад < [email protected] [email protected] > написал:

@tsvet omirhttps: //github.com/tsvetomir , это проблема с Angular, а не с TypeScript.

Наследование - плохое решение того, что по сути является прославленной директивой #include.

Да, наследование - плохое решение, но проблема в первую очередь в использовании классов. У них есть свое место, но оно очень ограничено. Классы JavaScript довольно слабые и невыразительные по сравнению с классами на других языках.

Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на Gi tHubhttps: //github.com/Microsoft/TypeScript/issues/563#issuecomment -234753589 или отключите чтение этого сообщения XJv7tc9LB0tPsIks5qYtH4gaJpZM4CcixK.

Я никоим образом не считаю эту проблему специфической для Angular. Это частный случай, в котором, возможно, помогли частичные классы.

Запрос исходит из прошлого опыта работы с C # и отсутствия знакомых функций. Сравнение неизбежно, поскольку TypeScript не пытается дистанцироваться от .NET. Пользователи ожидают некоторого паритета функций.

Запрос исходит из прошлого опыта работы с C # и отсутствия знакомых функций. Сравнение неизбежно, поскольку TypeScript не пытается дистанцироваться от .NET. Пользователи ожидают некоторого паритета функций.

@tsvetomir, как давний разработчик .NET и страстный программист на C #, я в корне не согласен.

Цитированная вами статья не является авторитетной и не отражает никаких официальных материалов от команды TypeScript.

Более того, это прямо противоречит философии дизайна TypeScript, изложенной @ahejlsberg в многочисленных беседах, интервью и постах.

Кроме того, точка зрения процитированной статьи отражает фундаментальное непонимание природы TypeScript.
По иронии судьбы, общие черты C # и TypeScript на самом деле такие же, как и у JavaScript.
Эти общие черты не являются программированием на основе классов, а скорее являются конструкциями, такими как замыкания и функции более высокого порядка.

C # или нет, нам нужны частичные классы, которые помогут TypeScript приблизиться к функциям прототипа Javascript.

@ yahiko00
TypeScript уже имеет все функции прототипа JavaScript.
Это суперсет.

Есть несколько проблем с понятием частичных классов в TypeScript, в том числе:

  1. В отличие от C #, определения классов TypeScript (JavaScript) являются императивными, а не декларативными. Они кажутся декларативными, но это не так. Это означает, что они зависят от детерминированного порядка выполнения.
  2. Модульная система ECMAScript основана на импорте физических, а не логических единиц кода.
    Например, допустим, у меня есть

_app / мой-класс-часть-1.ts_

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

а также
_app / мой-класс-часть-2.ts_

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

Как я могу импортировать этот класс? Поскольку я не могу импортировать ни из одного модуля, давайте представим гипотетическую абстракцию в TypeScript, которая позволяет мне писать
_app / main.ts_

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

Что бы это значило? Даже если компилятор TypeScript может сделать вывод, что _app / my-class-part-1.ts_ должен быть загружен до _app / my-class-part-2.ts_, он не может сделать импорт отдельных частей недействительным и не может гарантировать они импортируются в правильном порядке загрузчиком асинхронных модулей, например RequireJS, или тем, что в конечном итоге будет реализовано в браузерах.

Само понятие частичных классов принципиально расходится с модульной системой ECMAScript.

Обновление: потребуется выполнить произвольно сложный вывод зависимостей и испустить очень странный JavaScript.

TypeScript уже имеет все функции прототипа JavaScript.
Это суперсет.

Я должен был лучше выразиться. Конечно, все мы знаем, что TypeScript - это надмножество JavaScript. Но я хотел сказать, что частичные классы помогут классам TypeScript приблизиться к прототипам JS. На данный момент у нас могут быть просто «статические» и не расширяемые классы, тогда как с помощью прототипного подхода мы можем расширять классовые функции.

Я согласен с тем, что импорт частичных классов приведет к расширению текущего синтаксиса ES6. Мы могли представить себе что-то вроде этого:

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

Классы TypeScript основаны на классах ECMAScript и, следовательно, страдают от недостатка выразительности последних. Рассмотрите возможность использования функций, модулей, «пространств имен» или простых объектов для выполнения того, что вам нужно.

Функции - это то, чего мы хотим избежать с помощью понятия класса.
Модули или пространства имен обеспечивают простую расширяемость и не обладают такой выразительностью, как классы TypeScript. Кроме того, нельзя создать экземпляр пространства имен или модуля. Они не служат той же цели, что и классы.

@aluanhaddad Я понимаю, что вы имеете в виду в отношении модулей. Возможное решение - разрешить export partial class только одному из файлов, в то время как остальные будут содержать только определения partial class . Вы импортируете его из того места, где он был экспортирован. Из вашего примера:

_app / my-class.ts_

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

_app / мой-класс.part.ts_

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

_app / main.ts_

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

Чего не хватает, так это синтаксиса, чтобы компилятор знал, где найти части. Это не проблема C #, где вы обрабатываете все файлы одновременно. Это может быть особый синтаксис, такой как тот, который используется для слияния деклараций .

Для частичных классов не требуется разрешать произвольные определения. Например, они могут быть ограничены только методами, свойствами и конструкторами. Это делает порядок выполнения несущественным и позволяет компилятору выдать ошибку, если член дублируется.

Я не понимаю, почему расширение модуля не подходит для этих сценариев. У нас это очень хорошо сработало с Rxjs5.

Вы просто создаете свой базовый класс, а затем с помощью генерации кода генерируете все необходимые расширенные методы поверх базового класса. Не имеет значения, красив ли сгенерированный код, люди не должны его писать.

Это идеальная замена частичным классам? Я полагаю, что нет, но я не вижу частичных классов, если ECMAScript не решит, что они нужны JavaScript. Сама по себе семантика того, как частичные классы связаны между собой между внешними модулями, причиняет мне боль.

@ david-driscoll, чтобы сделать его еще лучше, в добавленных методах может быть объявлен аргумент this: для более строгой проверки типов.

В приведенном примере Module Augmentation :

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

Ага. Rxjs пока не использует его, потому что это функция 2.0, а версия 2.0 еще не выпущена. Позже это будет в моем хит-листе.

Я понимаю, что вы имеете в виду по поводу модулей. Возможное решение - разрешить экспортировать частичный класс только одному из файлов, в то время как остальные будут содержать только определения частичного класса. Вы импортируете его из того места, где он был экспортирован. Из вашего примера:

_app / my-class.ts_

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

приложение / мой-класс.part.ts

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

Мне нравится синтаксическая концепция, но проблема в том, что вы только что представили новый _kind_ исходного файла, который используется как своего рода неявный модуль, выглядит как сценарий и должен быть загружен определенным образом.

Чего не хватает, так это синтаксиса, чтобы компилятор знал, где найти части. Это не проблема C #, где вы обрабатываете все файлы одновременно. Это может быть особый синтаксис, такой как тот, который используется для слияния деклараций.

Объединение деклараций работает для _declarations_, а не для исходных файлов.

Для частичных классов не требуется разрешать произвольные определения. Например, они могут быть ограничены только методами, свойствами и конструкторами. Это делает порядок выполнения несущественным и позволяет компилятору выдать ошибку, если член дублируется.

Все это обычно зависит от порядка.

Изменить: то есть их определение изменяет класс.

Возможно, кто-то уже упоминал об этом, но еще один полезный случай - когда вы генерируете часть класса из некоторого генератора кода, а затем хотите вручную добавить еще код непосредственно в свою реализацию.
Вы действительно не хотите заниматься смешиванием или производным классом.
Это по-прежнему один класс - просто часть генерируется автоматически, другая часть вручную.

Кстати: если кому-то нужно сгенерировать типы TS из классов C #, вы можете проверить TypeScriptBuilder

Спасибо, что обдумали это!

Я собираюсь опробовать ваш TypeScriptBuilder;)

Мое предложение...

Что, если компилятор не попытался найти все части? Что, если бы мне пришлось импортировать другие части и все контролировать?

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

Потом...

  1. Если я хочу использовать MyClass мне нужно импортировать его из MyClass.ts
  2. При компиляции MyClass.partial.ts сгенерирует javascript, который расширяет MyClass.prototype как показано здесь ранее. Но он экспортирует функцию, которая получает прототип для расширения.
  3. MyClass.partial.ts импортируется в MyClass после того, как MyClass определено и вызывается функция расширения.

Кстати, ничто не мешает мне напрямую сгенерировать скомпилированный код. Но я теряю все величие Typescript.

@svallory Я думаю, что это определенно правильный подход. В частности, поскольку вы говорите, что сгенерированный код будет экспортировать функцию, которая вызовет расширение, он будет работать даже с асинхронным импортом, потому что функции могут вызываться в необходимом детерминированном порядке.

В более общем плане, и я забываю о проблеме, в которой на нее ссылались, если присвоения прототипу класса повлияли на форму класса, могло бы быть много преимуществ. Это улучшит библиотеки миксинов, сделает декораторы гораздо более полезными и в целом будет способствовать менее вертикальной иерархии.

Компилятору может быть сложно отследить все это, но, вероятно, его можно заставить работать, если будет явным, как вы предлагаете.

Это было бы усовершенствованием системы типов, при этом варианты использования частичных классов выпадали из этого усовершенствования.

У меня получилось так:

У меня получилось так :
 файл1 . ts
 interface ifoo {
 а ( ) : недействительно ;
 }
 class foo реализует ifoo {
 a ( ) { / * сделать что-нибудь * / }
 }
 файл2 . ts
 /// <reference path = "file1.ts" /> // не обязательно
 interface ifoo {
 b ( ) : недействительно ;
 }
 фу . прототип . b = ( ) = > { / * что-нибудь сделать * / }
 файл3 . ts
 /// <reference path = "file1.ts" /> // не обязательно
 interface ifoo {
 c ( ) : недействительно ;
 }
 ( < ifoo > foo . прототип ) . c = ( ) = > { / * что-нибудь сделать * / }
 потребитель . ts
 /// <путь для ссылки = "file1.ts" />
 /// <путь для ссылки = "file2.ts" />
 /// <путь ссылки = "file3.ts" />
 пусть f = new foo ( ) ;
 f . а ( ) ;
 f . б ( ) ;
 f . c ( ) ;

Модуль Augmentation по-прежнему решает здесь 90% проблемы. Учитывая примеры @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;
    }
}

Дополнение модуля было бы чем-то вроде ...

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

Он немного более подробный, чем частичный класс, но на самом деле частичный класс был бы просто синтаксическим сахаром для этой реализации.

Единственные проблемы, которых сегодня у вас нет, были бы:

  • Вы не можете получить доступ к закрытым или защищенным полям с помощью синтаксиса this: MyClass .

    • Это можно легко решить в сгенерированном коде путем преобразования в any .

    • Возможно, это что-то, что можно добавить, например, чтобы вы могли получить доступ к защищенным членам this . Вокруг этого могут быть другие правила.

  • Сгенерированный код выглядит немного более устрашающим, но для потребителя, если что-то было дополнено, они никогда не заметят разницы.

РЕДАКТИРОВАТЬ: имейте в виду, что это синтаксис TS 2.0+.

@ david-driscoll Я согласен. Я думаю, что это проще, и мне нравится, что он не вводит дополнительный синтаксис. Как вы знаете, я против частичных классов как особой языковой особенности, но я думаю, что было бы полезно, если бы TypeScript отслеживал назначения прототипов в целом и соответствующим образом уточнял форму объекта. Я думаю, что ваш подход - правильный способ разделить классы по нескольким файлам.

@wongchichong В вашем примере используются имена глобальных классов и /// <reference path="..."/> . Я не думаю, что это будет хорошо масштабироваться, хотя вы могли бы использовать пространство имен для улучшения ситуации.
Код, написанный в виде модулей (то есть внешних модулей), - это совсем другое дело, потому что он включает в себя загрузчик, который должен знать о зависимостях, которые зависят от порядка и неявны. Вот почему предложение @svallory ценно, оно требует минимального количества ручного подключения, которое делает зависимости явными.

Что делать, если кто-то не пользуется модулями?

Частичные классы @pankleks все еще плохая идея.

Чтобы прояснить, я имею в виду, что они являются плохой идеей для такого языка, как JavaScript, где объявления классов являются императивными, а не декларативными. Даже если вы используете пространства имен, вам придется разбить свой код на несколько файлов, что означает, что вам придется зависеть от порядка тегов сценария в этих файлах, чтобы это вообще работало.

В таком языке, как C #, частичные классы работают хорошо, но это потому, что они фундаментально отличаются от классов в JavaScript.

Важно помнить, что в JavaScript объявления классов даже не поднимаются, не говоря уже о декларативных.

За исключением ES6, понятие «класс» определяется TS. JS вообще не имеет понятия о классе.
Так что TypeScript должен решить, каким может быть класс, не так ли?

За исключением ES6,

Мы не можем запретить ES6 при обсуждении функций языка TS, потому что одна из целей TS - следовать текущей / будущей спецификации ES .

Правда что :/

Для больших систем с классами, сгенерированными кодом, т. Е. Сущностями, клиентами уровня обслуживания и т. Д., Частичные классы в соответствии с дизайном C # предоставляют очень элегантное решение, когда сгенерированный код необходимо расширить с помощью дополнительных состояний и поведения. В моем нынешнем проекте, где у нас есть ~ 500 классов генерации кода, я очень скучаю по этой функции.

Я до сих пор не понимаю, почему некоторые люди так враждебно относятся к функции, которая доказала свою полезность в другом месте, за исключением того, что говорят: «Это плохая идея».

Похоже, на esdiscuss это не особо обсуждалось, единственная найденная мною ветка содержит 9 сообщений.

@ yahiko00 Не то чтобы я враждебно относился к этой концепции. Как вы говорите, это очень полезно для таких языков, как C #.

Как увлеченный программист на C #, я считаю, что частичные классы очень полезны для определенных задач. Однако они не мешают другим аспектам языка. Они не нарушают ключевые связанные абстракции, такие как пространства имен, сборки и объявления произвольно упорядочиваемых типов.

С другой стороны, у JavaScript есть модульная система, которая в лучшем случае только зарождается. Классы - это новое явление, и я бы сказал, что пока еще не очень выразительная особенность языка, им нужно время, чтобы вырасти.

Если мы сделаем шаг назад и рассмотрим методы расширения, еще одну мощную и гораздо более полезную функцию C #, я бы хотел, чтобы они были добавлены в JavaScript, но при этом возникнут фундаментальные проблемы, которые еще предстоит решить.

Когда мы достигнем точки, когда частичные классы могут быть указаны без нарушения или строгих ограничений гораздо более фундаментальных концепций, таких как модули, я буду полностью за их добавление.

Тем не менее, как указывает @SaschaNaz , это _должно быть решено в ECMAScript.

Может быть, любой желающий может открыть новую тему на http://esdiscuss.org и разместить здесь URL-адрес, чтобы мы могли продолжить обсуждение там 😄

PS: Или на более-GitHub-подобном ES Discourse .
PS2: Или на WICG также-GitHub-like-and-more-active.

Мне тоже хотелось бы увидеть какую-нибудь конструкцию, позволяющую генерировать код для TS. Частичные классы отлично подходят для этого в C #.

Возможно, мне что-то не хватает, но мне кажется, что основная проблема заключается в расширении класса с доступом к «частным» переменным откуда-то, кроме конструктора, предпочтительно из другого файла.

Основываясь на обсуждении, кажется, что большая часть «безопасных» инноваций TS уже реализована. Теперь мы просто ждем, чтобы увидеть, что сделает руководящий орган ES, потому что мы не хотим ничего нарушать в будущем. Совершенно верно, но немного обидно, потому что я надеялся на более быстрый поезд с TS.

Обязательным условием является поиск хорошего обессахаривания :

Хотя раньше я думал, что частичные классы были довольно глупыми, потому что вы могли просто добавить к классу напрямую, я вижу привлекательность, учитывая, что это потребует от вас сделать ваши методы неперечисляемыми плюс исправление суперсвязки (последнее из которых в настоящее время невозможно в ES6).

Сначала нам нужно найти хороший способ избавиться от классов в целом в композиционные, императивные примитивы, такие как старый toMethod. Но как только мы это сделаем, добавление еще немного сахара, например, частичных классов, может быть разумным, в зависимости от того, насколько эргономичными - или нет - окажутся эти примитивы.

Не уверен, что именно это означает, но, вероятно, вот так:

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

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

(Крест размещен на esdiscuss )

Кажется, есть довольно простой способ реализовать частичный класс в чистом JS (с ES2017 getOwnPropertyDescriptors ). Люди ES могут подумать, что синтаксический сахар даже не понадобится только для удаления этого крошечного кода.

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

Для использования этого в TS, конечно же, требуется дубликат interface для расширения существующего типа класса.

@SaschaNaz Я ожидаю, что свойства, добавленные в конструктор через getOwnPropertyDescriptors, будут скрыты для «автозаполнения» IDE (поскольку они добавляются во время выполнения). Помните, Typescript родился как JavaScript с «самовнушением» (иногда люди называют его «строго типизированным», но на самом деле им нравится «самовнушение»).

PS Зачем людям частичные классы - кодогенерация. Но опять же, генерация кода должна поддерживаться как IDE (простой «инструмент», настраиваемый на исходный файл, иерархический результат в проводнике файлов, поддержка T4 или аналогичного «общего генератора кода»), так и языком (частичные классы, методы расширения). Только поняв это, можно понять, о чем на самом деле спрашивают разработчики. Если вы не видите полной картины - конечно «частичные классы» выглядят «чушью».

@aluanhaddad : но разве основная функция TypeScript не состоит в том, чтобы предоставить нам языковые элементы, которых нет в JavaScript? Почему - плохая идея разделить класс на два частичных файла TypeScript, которые будут скомпилированы в один файл JavaScript?

Я действительно не вижу проблемы с предоставлением инструмента сообществу. И я на 100% уверен, что найдутся разработчики, которые злоупотребят им. Но также и разработчиков, которые точно знают, как его использовать. Но разве у нас не бывает такой же ситуации с любой конструкцией любого языка?

+1 для целей генерации кода. И я согласен с @rpokrovskij , почему мы выбираем TypeScript, я также могу использовать JavsScript напрямую, просто потому, что он похож на C # и имеет поддержку ide.

@greendimka Я не знаю, как это могло бы работать, не

@ xiexin36 TypeScript
Между JavaScript и C # есть общие черты, но их представления о class совершенно разные.

@ xiexin36 для генерации кода вы все равно можете использовать расширение модуля (как я уже отмечал ранее в этом потоке ). Синтаксически это не так просто, как частичные классы, но вы можете выполнить ту же задачу.

@aluanhaddad Что ж, это работает очень просто: частичные классы никоим образом не меняют структуру кода. Они просто позволяют нам писать определение одного и того же класса в нескольких файлах (которые в любом случае компилируются в один JS).

@aluanhaddad Строго говоря: javascript не имеет понятия классов (классы ES6 просто предоставляют синтаксический сахар). Итак, если мы попытаемся следовать концепции того, чтобы TypeScript был таким же слабым, как JavaScript, не должны ли мы выбросить классы из TypeScript? Разве мы не должны просто вернуться к JavaScript?

@greendimka Классы ES6 и классы TypeScript - это одно и то же.
TypeScript не нацелен на добавление дополнительных конструкций в среду выполнения JavaScript.

Классы ES6 очень тусклые. Это не означает, что JavaScript не является мощным языком (это так). TypeScript обеспечивает статическую типизацию для выявления ошибок во время разработки, предоставляет высококачественные инструменты, обеспечивает чрезвычайно точную и строгую спецификацию интерфейсов компонентов и в целом повышает производительность.

Классы ES6 очень тусклые, но для TypeScript введение собственного понятия классов было бы прямым нарушением его целей проектирования.

Просто избегайте занятий и ваша проблема будет решена.

Что ж, это работает очень просто: частичные классы никак не меняют структуру кода. Они просто позволяют нам писать определение одного и того же класса в нескольких файлах (которые в любом случае компилируются в один JS).

И сломать почти все известные человеку инструменты? Нет, спасибо.

@ david-driscoll Спасибо, я попробую, но я также использую Angular 2.0, интересно, сгенерирую ли я код с помощью
@Input или @Output , будет работать, все равно попробую.

@aluanhaddad, это больно видеть, как вы повесились на повторении «классов? javascript не имеет классов». Уберите классы из головы, оставьте только JavaScript, а теперь слушайте: разработчики, использующие генерацию кода, хотят иметь одну единицу компиляции в нескольких файлах. На этом языке нет классов, только функции? Хорошо, нам нужны частичные функции. И JavaScript почти готов для этого: функции там можно использовать до объявления. Конечно, там мы можем встретить другие проблемы (например, чтобы убедиться, что все части / файлы загружены в среду исполнения), но они разрешимы, и на самом деле мы хотим иметь частичные единицы компиляции в TypeScript, а не в JavaScrpipt. Машинопись к этому готов на все 100%.

И, кстати, команда TypeScript также предоставила нам частичные функции! И, пожалуйста, скажите то же самое команде C #, поскольку у нас пока есть «функции в функциях». :)

Просто чтобы убедиться, что вы понимаете, что частичные функции имеют очень конкретное значение, и то, что вы описываете, не является частичными функциями при любом уровне воображения?
Генерация кода полезна, я тоже ею пользуюсь.

@aluanhaddad . Вы понимаете термин «одна единица компиляции в нескольких файлах»? Javascript, конечно, не язык компиляции, но а) функция должна быть полностью проанализирована перед выполнением, и мы можем назвать это «компиляцией» б) Typescript - это язык компиляции.

Чтобы помочь вашему воображению:

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

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

Я также не часто использую частичные классы: чтобы использовать их эффективно, нам нужно хранить параметры генерации кода в «ручной» части, а для чтения этой конфигурации у нас должен быть хороший синтаксический анализатор языка (а не рефлексия). Но так оно и есть: частичные классы - это лишь один из многих инструментов, облегчающих создание кода.

@rpokrovskij
Прошу прощения, если я показался враждебным. Я не имел в виду обиду или неуважение.

В вашем примере есть аспекты, которые требуют достаточно формального описания сами по себе. Это предложение изначально не касалось прямого включения сценариев генерации кода.

@aluanhaddad ....
«Просто избегайте занятий и ваша проблема будет решена» - серьезно, это решение проблемы ?! Возможно, если я откажусь от использования JavaScript - я смогу решить проблему всей этой дискуссии. Тоже решение, не так ли? К сожалению, у нас есть этот дерьмовый язык в нашей вселенной, потому что ему посчастливилось попасть в Интернет много лет назад. Все равно это оффтоп.

«сломать почти все известные человеку инструменты» - как это возможно ??? Как?!?! Я пытаюсь представить, что что-то сломает, используя частичные классы в TypeScript, но не могу. Я просто не могу. Пожалуйста, помогите мне на каком-нибудь примере из реальной жизни.

А теперь представьте, что вы построили машину для перевозки ящиков. Работает без проблем. Вы нанимаете рабочего, который загружает машину. Рабочий берет сразу по два ящика и кладет в машину. Тем не менее все работает нормально.
Однажды рабочий уходит в другую компанию, а вы нанимаете новую. Новый берет одну коробку, загружает ее, берет еще одну коробку и загружает ее тоже. По логике: все ок. Но ты бегаешь и кричишь: стоп, стоп, у нас сбой системы! :)

«Просто избегайте занятий и ваша проблема будет решена» - серьезно, это решение проблемы ?! Возможно, если я откажусь от использования JavaScript - я смогу решить проблему всей этой дискуссии. Тоже решение, не так ли? К сожалению, у нас есть этот дерьмовый язык в нашей вселенной, потому что ему посчастливилось попасть в Интернет много лет назад. Все равно это оффтоп.

В ES2015 наверняка много чего не так. В ES5.1 наверняка было много неправильных вещей. На мой взгляд, нехватка занятий была не из таких вещей. Я хочу сказать, что, используя такие методы, как шаблон модуля раскрытия, вы устраняете необходимость в частичных классах. TypeScript предоставляет сахар для раскрытия шаблона модуля, пространств имен.

«сломать почти все известные человеку инструменты» - как это возможно ??? Как?!?! Я пытаюсь представить, что что-то сломает, используя частичные классы в TypeScript, но не могу. Я просто не могу. Пожалуйста, помогите мне на каком-нибудь примере из реальной жизни.

Если вы используете модули ES, это проблематично. Любой инструмент, который полагается на физические артефакты компилятора, имеющие соответствие со своими некомпилированными аналогами, будет подпадать под это действие. Тем не менее, TypeScript мог бы отрегулировать это в emit, огромное изменение, но тогда он будет генерировать JavaScript, который не соответствует TypeScript, который вы написали.

Так что, по сути, развитие языка сдерживается инструментами, которые никогда не будут развиваться без эволюции языка.
Извините, но я вижу здесь только поиск причины не развиваться.

Какие преимущества дают частичные классы, которые вы уже не можете сделать с языком по-другому? Конечно, это хороший синтаксический сахар, но в чем истинная выгода? Есть ли конкретный пример, кроме как сделать его более похожим на C #?


В сторону: для записи я начал как разработчик C #, а оттуда перешел на JavaScript / C #. Как только было объявлено о TypeScript 0.8, меня продали не потому, что это был C # для JavaScript, а потому, что он использовал строгий JavaScript и делал его лучше для разработчиков, которым нравятся сильные типы C #.

TypeScript не является C # и не должен строго наследовать функции C # только потому, что они есть в C #. Так же, как C # не должен наследовать функции TypeScript. Я не могу сосчитать, сколько раз мне требовались такие функции, как типы объединения в C #, потому что они так хороши для TypeScript, но пока не подходят для этого языка.


До сих пор я читал «Генерация кода» и «Организация кода».

Генерация кода, посмотрите на Расширение модуля , я здесь назвал некоторые недостатки, но я уверен, что их можно исправить.

Код организации, я думаю, использовать регионы (смеется)? Есть много других способов организовать код. Если у вас есть один класс God (по какой-либо причине), вы всегда можете разделить его на множество классов God. На первый взгляд класс god - это импорт, который все видят и используют, но под покровом он состоит из многих классов, возможно, как свойств класса god. Если вы используете частичные классы для организации, я бы сказал, что вам нужно немного реорганизовать свой код, чтобы попробовать и SOLID ваш код.

Я смог приспособить дополнения к большинству сценариев генерации кода, с которыми я сталкивался до сих пор.

Для Omnisharp-клиента я использую расширение интерфейса здесь , где OmniSharp.Api.V2 является формируемый посредством отражения в C #. Внизу файла у меня есть инструмент, который отменяет все методы для этого интерфейса.

Для RxJS мы используем to для сопоставления всех наших методов , находящихся в Observable .

Я просто не могу обдумывать некоторые проблемы с частичными классами. Например, если нам даны 3 частичных класса.

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

Область действия: глобальный модуль (с использованием namespace / module )

Глобальная компиляция - это самый простой способ. Поскольку TypeScript уже объединяет пространства имен вместе.

Если мой tsconfig.json содержит только пути к файлам для Class1 и Class2, тогда все, что находится в Class3, автоматически не объединяется.

  • Сначала это кажется ошибкой пользователя, но не сразу становится понятно, почему не включены методы из Class3. Это огромная проблема юзабилити, которая приведет к путанице.

Объем: Внешние модули (импорт / экспорт)

При внешней компиляции каждый файл действительно считается модулем (или адской сборкой в ​​мире .NET), потому что каждый модуль имеет явный список всех его зависимостей (импорт) и всех его общедоступных API (экспорт).

Как вы рассуждаете об этих сценариях?

Учитывая только одну ссылку на файл, каково поведение класса?
Он автоматически объединяет Class2 и Class3?

  • Экспорт одного и того же значения по умолчанию для Class1, Class2 и Class3?
  • Какой файл записан на диск?
  • Как будет выглядеть базовый JavaScript для всех поддерживаемых форматов (amd, umd, commonjs и т. Д.)
// somefile.ts
import { MyAwesomeClass } from 'MyAwesomeClass1';

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

Нужно ли явно импортировать все экземпляры класса? (Если мы сделаем это, я потерплю поражение).
Что произойдет, если вы импортируете все в один файл, но не в другой, в методы, которые вам нужны?
Это могло привести к действительно странным ошибкам, учитывая сложный граф зависимостей, в котором класс используется один раз, без дополнительных частей. Могут ли значения быть увеличены? или методы волшебным образом не сработают, пока не будет загружен другой файл, который их вводит?

// 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 Здесь только мои два цента:

Конечно, это хороший синтаксический сахар, но в чем истинная выгода?

Разве хороший синтаксический сахар не является сутью любого языка? С оперативной точки зрения все, что нам нужно, это циклы, if-clauses и итерация, чтобы делать все. Остальное - просто приятный синтаксический сахар, чтобы быть более продуктивным и обеспечивать другие аналогичные нефункциональные качества.

И хотя языки, как правило, хороши в оперативной части инструктирования компьютера, они обычно не справляются с организационной частью написания кода. У нас есть эти специальные культурные артефакты, такие как объекты, классы, наследование и т. уметь с ней хорошо работать.

В TypeScript мне нравятся две вещи: 1. Он позволяет нам использовать типизированный javascript, так что сразу становится возможным создавать сложные системы на клиенте. 2. Система типов чрезвычайно выразительна (потому что она была вынуждена иметь возможность выражать все виды шаблонов в javascript, которые люди могут писать без ограничений системы типов).

Но что касается структурирования кода, я не думаю, что он намного менее отстой, чем любой другой язык. И я хотел бы, чтобы мы могли быть здесь более инновационными. Предоставьте пользователям больше свободы для экспериментов с организационной частью языка. Открытость и стремление к новой структурной выразительности вместо того, чтобы быть ограничивающим и подозрительным.

Частичные классы не только очень удобны для генерации кода, но и увеличивают свободу организации в целом. Свобода, которая нам нужна, чтобы выбраться из этой ямы кодовой организации.

@ david-driscoll в чем смысл использования частичных файлов? И еще раз: генерация кода.
Что не так с предложенным вами решением? Это обходной путь. Еще один обходной путь (и довольно грязный обходной путь). У нас уже есть дерьмовый язык (javascrtipt), в котором есть обходные пути. Вместо того, чтобы думать о своих основных задачах, разработчики javascript тратят много времени на обдумывание обходных путей. С вашей предложенной философией мы можем отбросить весь язык TypeScript. Потому что (сюрприз, сюрприз) всю систему типов можно смоделировать на чистом javascript - используя обходные пути. Зачем заботиться о более выразительном языке (например, Typescript), если вы можете смоделировать что угодно с обходными путями? Зачем писать одну строчку кода, если одно и то же можно написать двадцатью строками? Верните зарплаты на основе количества написанных строк кода! Короткий выразительный код для кисок, не так ли?

И нет: запрос на наличие частичных файлов в TypeScript не основан на идее, что они есть в C #. Я не верю, что в TypeScript должно быть что-то только для того, чтобы это было, потому что это есть в другом языке.

И напоследок: почему вы думаете, что если вы чем-то не пользуетесь - это никому не нужно?

@greendimka Я не пытался разжечь враждебность: shield :. Я просто пытаюсь понять сценарии или, если есть такие, о которых я не знаю, которые, честно говоря, вполне возможны!

Теперь имейте в виду, я не в команде TypeScript, я просто сторонник, но команда здесь довольно молчалива. Команда вполне может вмешаться, когда у них есть что сказать, и я подчинюсь им, если они захотят это сделать.

Один из ключевых клиентов, которого придерживается команда TypeScript, - это попытаться сохранить синхронизацию движения TypeScript с направлением ECMAScript и избежать создания каких-либо конкретных функций, которые могут привести к их отклонению от общего направления развития JavaScript.

Дело не в том, что я бы не стал его использовать (возможно, если бы он существовал). Проблема в том, что в этом вопросе частичные классы могут иметь предостережения, поэтому кажется сомнительным, что это будет реализовано в ближайшее время. Вероятно, он будет оставаться таким некоторое время, пока: а) он не станет обязательной функцией для заинтересованных сторон, или б) предложение перейдет на более поздние стадии TC39.

Я не рассматриваю расширение модуля как обходной путь. Он позволяет расширить систему типов и позволяет использовать многие сценарии генерации кода, которые были подробно описаны в этом потоке. Это шаг к другим языковым возможностям.

  • Это некрасиво? Абсолютно уродливые, как все, черт возьми. Тем не менее, если вы создаете код, в чем разница? Только ваш инструмент генерации должен беспокоиться о том, как расширить класс. После того, как вы запустите инструмент генерации кода и включите свои модули, вы получите полные ошибки intellisense, компиляции и т. Д.

  • Это идеально? Отнюдь не! Я вижу гораздо более распространенный сценарий, когда миксины нуждаются в поддержке на уровне языка. И я думаю, что кое-что из предложенных здесь декораторов имеет смысл, хотя, опять же, нет никакого конкретного предложения.

@ david-driscoll, извини за грубость. Я действительно склонен стоять на стороне позитивного прогресса.

Я бы хотел, чтобы статические реализации можно было поместить в частичный класс.

Поскольку не похоже, что это скоро осуществится, я хотел бы предложить обходной путь, который мы нашли. Есть несколько подобных комментариев, но я не видел ни одного с такой спецификой.

Мы хотели бы, чтобы Partials использовался в сценарии генерации кода, где мы хотим иметь возможность расширять наш сгенерированный код TypeScript. Однако мы создаем модели, у которых есть взаимозависимости, поэтому наследование от наших классов не работает в традиционном смысле. Сгенерированные классы, которые ссылаются на другие классы, в конечном итоге не имеют правильного типа объекта, на который ссылаются, и мы бы получили сложный фабричный шаблон для создания правильных типов экземпляров.

В итоге мы изменили наши сгенерированные классы на базовые, но только в объявлении класса. Затем мы создали классы-заглушки, унаследованные от наших новых базовых классов. Затем мы могли бы расширить эти новые классы, и они унаследовали бы все свое содержимое от базового класса.

Например:
Если бы мы сгенерировали класс Person, мы бы теперь сгенерировали PersonBase. Весь сгенерированный код попадает в этот класс. Мы также создадим пустой класс Person, который расширяет PersonBase. Этот класс может быть сгенерирован только один раз. Затем мы помещаем весь наш сгенерированный код в PersonBase, а весь пользовательский код вручную переходит в Person. Все сгенерированные ссылки на Person остаются как Person, а не PersonBase. Таким образом, весь IntelliSense продолжает работать.

Файл 1: Базовый класс

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

Файл 2: Фактический класс

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

Это решило нашу проблему частичного класса с сгенерированным кодом. Престижность Эндрю Скотту за идею.

Мне это тоже очень нравится, так как я генерирую код с помощью генератора кода, и мне нужно добавить в него некоторые изменения. Частичные классы позволят регенерировать код без необходимости повторного добавления изменений позже (которые могут быть большими или сложными)

Есть важная причина для частичных классов, о которой я еще не думаю, что упоминалось: постепенный переход JS на TS. В Javascript, особенно до ES6, классы могут выглядеть как набор:

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

И они могут быть распределены по нескольким файлам без какой-либо специальной обработки. Преобразование подобной кодовой базы в современный TypeScript в настоящее время требует перемещения функций, чтобы все члены класса находились в одном файле, что может быть довольно агрессивным изменением. С частичными классами миграцию можно было выполнить, используя только локальные изменения.

@RyanCavanaugh Вы упомянули, что все, что здесь запрошено, можно синтаксически обработать с помощью другого синтаксиса TypeScript:

Я думаю, что в предложении по миксину гораздо больше открытых вопросов. Предложение частичного класса здесь - это просто кодификация вещей, которые уже разрешены в TypeScript, только с некоторым синтаксическим сахаром (эту функцию можно буквально сделать как синтаксическое переназначение на то, что у нас уже есть).

Итак, как это сделать в 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() );
    }
}

Транспортировка приведенного выше приведет к созданию одного JS-класса для Contact, примерно так:

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

Обратите внимание, что в конце концов мне нужен один класс с именем «Контакт», и я не хочу, чтобы два отдельных класса были подклассами третьего.

@cosmoKenney

Вот пример того, что будет работать. Это не так синтаксически исправлено, как partial , однако оно работает и является очень функциональным решением.

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

В качестве альтернативы вы также можете создать код сгенерировать интерфейс и использовать расширение интерфейса для его реализации.

Также могут быть способы использовать новую функциональность Mixin, однако я сам еще недостаточно углубился в это, чтобы дать квалифицированный ответ.

@ david-driscoll Вау. Моя голова просто взорвалась.

Интересно, получится ли у сообщества форк, который просто поддерживает частичные классы, не избавляясь от проблемы.
Пока не слышно никаких реальных аргументов против частичных классов. Всего несколько причин, чтобы оправдать лень.

@greendimka

Интересно, получится ли у сообщества форк, который просто поддерживает частичные классы, не избавляясь от проблемы.
Пока не слышно никаких реальных аргументов против частичных классов. Всего несколько причин, чтобы оправдать лень.

Не уверен, что вы пытаетесь здесь сказать. Но если в сообществе сложилось общее ощущение, что мы можем достичь тех же результатов, используя существующие языковые функции, то мне просто нужны несколько примеров того, как это сделать. То, что написал @ david-driscoll выше, выходит за рамки моего понимания. И, похоже, не решает проблему полностью. На самом деле, насколько я могу судить, он неверно истолковал мои намерения.
Моя основная забота - это код, генерирующий скалярные свойства класса, такие как pojo или класс модели. Но мне нужно иметь возможность в отдельном файле или даже в том же файле, но не в пределах определения первичного класса, добавить свойства ассоциации (чтобы я мог вставить новое первичное определение, сгенерированное кодом, поверх оригинала).
Например, мой класс Contact из приведенного выше получит код, сгенерированный с именем firstName и lastName. Но в моем отдельном определении я бы добавил определение и инициализацию коллекции объектов Address.
Мой генератор кода знает только атрибуты класса, а не ассоциации, поэтому он не может кодировать генерацию коллекции адресов - как это было сделано в примере Дэвида.

@cosmoKenney было не совсем понятно, где должен существовать адрес. Простите меня за неверный контекст. Я просто пытаюсь помочь дать альтернативы. Существует множество функций, которые позволяют аналогичное поведение, не помещая что-то в компилятор, например частичные классы, которые имеют множество дополнительных соображений и потенциальных проблем для реализации обеих модульных систем (внутренних / внешних).

В вашем случае создание абстрактного базового класса или создание интерфейса будет работать, тогда ваш другой класс наследуется от этого. Тогда тот факт, что ваш класс был сгенерирован, все еще скрывается.

то есть:

// 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 предлагает интересное решение, но ему все еще не хватает простоты реальных частичных классов. Как показали мои личные эксперименты - разработчики просто не используют «частичные», сделанные таким образом. Хотя это хорошее временное решение, простота и удобство использования частичных файлов отсутствуют.

На данный момент я не знаю, как компилятор TypeScript работает внутри. Я могу только предположить, что он читает все исходные файлы, обрабатывает их и выводит файлы JS. Если это так - я не вижу проблем с чтением одного класса из нескольких исходных файлов. Если два или более исходных файла одного класса предоставляют конфликтующий код (например, одно и то же свойство определяется дважды), компилятор просто выдает исключение. Так же, как если бы вы написали синтаксически неверный код.

@greendimka , @ david-driscoll
Я попытался скомпилировать пример Дэвида сверху, и ts это не нравится:

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.

Кроме того, я согласен с @greendimka, что это слишком сложно, чтобы использовать его сотни раз в большом приложении.

@greendimka Я тоже не знаю, как работает компилятор, но не стоит недооценивать задачу. JavaScript не является .NET, и модули не доступны глобально (вы должны буквально импортировать их)

Я бы хотел, чтобы это было реализовано, но эта задача поднимает несколько вещей, о которых нужно тщательно подумать, например:

  • Если вы определяете часть класса в файле A, часть в файле B, какой из них должен содержать полный класс после пересылки? Новый должен быть сгенерирован?
  • Должны ли все файлы, содержащие частичный материал, экспортировать один и тот же класс?
  • Как избежать циклических зависимостей, если мне нужен основной файл на этих дополнительных?
  • Что, если я экспортирую в модуль больше вещей, чем только сам класс?
  • Проверка конфликтов имен?

и так далее...

@greendimka См. мои ответы ниже: (РЕДАКТИРОВАТЬ: это было нацелено на @WoLfulus , извините @greendimka):

Если вы определяете часть класса в файле A, часть в файле B, какой из них должен содержать полный класс после пересылки? Новый должен быть сгенерирован?

Я был бы совершенно счастлив, если бы файл A.js имел полное определение, а файл B был отключен. Может быть, с шаблонным комментарием. Большинство из нас все равно объединяют в пакеты, поэтому бесчисленное количество файлов .js, которые объединяются, в конце концов становятся, по сути, запоздалыми.

Должны ли все файлы, содержащие частичный материал, экспортировать один и тот же класс?

Это в значительной степени ответ на мой ответ на первый вопрос.

Как избежать циклических зависимостей, если мне нужен основной файл на этих дополнительных?

Я не знаю. Разве это не задача загрузчика модуля - распознать этот сценарий?

Что, если я экспортирую в модуль больше вещей, чем только сам класс?

Я не вижу в этом проблемы. Другими словами, если B.ts имеет как частичное определение, так и некоторые другие неполные классы, тогда B.js должен содержать шаблонный комментарий, как упомянуто выше, И определения неполных классов. Думаю, все просто.

Проверка конфликтов имен?

Как бы вы закончили конфликтом имен? Если вы имеете в виду класс, то ключевое слово "partial" решит эту проблему. Но тогда, если в двух других файлах определены классы с тем же именем, но без ключевого слова "partial", компилятор должен выдать ошибку о повторяющихся типах - как это происходит сейчас. Но если вы говорите о двух частичных определениях одного и того же класса, определяющих свойство или метод с одним и тем же именем, компилятор должен выдать ошибку о повторяющихся членах - как это происходит сейчас.

@WoLfulus , @cosmoKenney хорошо ответил. Просто чтобы лучше ответить на ваш первый вопрос (о файлах A и B): как разработчик языка вы имеете право определять правила. Под этим я подразумеваю, что файл A может иметь такой код «частичный класс X», а файл B может иметь такой код «частичный (fileX) класс X», который будет создавать fileX.js.

@cosmoKenney

Как избежать циклических зависимостей, если мне нужен основной файл на этих дополнительных?
Я не знаю. Разве это не задача загрузчика модуля - распознать этот сценарий?

TypeScript не имеет загрузчика модулей.

@greendimka

Интересно, получится ли у сообщества форк, который просто поддерживает частичные классы, не избавляясь от проблемы.
Пока не слышно никаких реальных аргументов против частичных классов. Всего несколько причин, чтобы оправдать лень.

Это плохая идея. Например, думали ли вы, как вы будете поддерживать вилку при добавлении динамического импорта?
Кроме того, было выдвинуто множество аргументов против частичных классов. Нарушение соответствия исходного кода между файлами TypeScript и JavaScript противоречит ключевому принципу языка. Я предполагаю, что для того, чтобы это можно было даже рассмотреть, функция должна будет включить сценарий, который будет бесконечно более ценным, чем частичные классы. Скорее всего, он все равно будет отклонен.

@WoLfulus , @cosmoKenney хорошо ответил. Просто чтобы лучше ответить на ваш первый вопрос (о файлах A и B): как разработчик языка вы имеете право определять правила. Под этим я подразумеваю, что файл A может иметь такой код «частичный класс X», а файл B может иметь такой код «частичный (fileX) класс X», который будет создавать fileX.js.

Я не думаю, что вы передаете свое сообщение. Это звучит так, как будто вы чувствуете, что любые ограничения, с которыми вы когда-либо сталкивались при выражении себя на любом языке, существуют по совершенно произвольным причинам.
Также не было ответа на вопросы

костюм пламени

TL; DR: этого не происходит. Если вы находитесь здесь, в комментарии № 189 и хотите выразить свое несогласие с этим, пожалуйста, сначала прочтите всю цепочку, потому что очень веские причины не добавлять это подробно описаны выше.

Наша интерпретация этой ситуации такова:

  • TypeScript уже имеет слишком много функций класса, специфичных для TS (объявления свойств конструктора, инициализаторы членов, незавершенные декораторы, модификаторы доступа, implements и т. Д.) Для чего-то, что действительно предназначалось для того, чтобы быть просто "типами ES6 +" . Частично это наша вина (наше первоначальное направление дизайна, как правило, было слишком ООП), а частично - ошибка ES6 (предложение класса много раз менялось пять лет назад, и в итоге у нас появились функции, которых не удалось). Добавление еще одной функции класса, специфичной для TS, - еще одна капля на спину верблюда, которой мы должны избегать, если можем.
  • Однозначно, будущее JavaScript - это модули. Частичные классы имеют смысл и хорошо работают в модели «глобального супа». Разрешение частичных классов, охватывающих несколько модулей, - это верный путь к катастрофе, поскольку порядок загрузки непредсказуем. Добавление функции, которая хорошо работает только в глобальной вселенной, - не лучшее использование времени.
  • Он есть в C #, но мы здесь не для того, чтобы изобретать C # заново. Шутки в сторону!
  • Сценарий «сгенерированного кода» не является убедительным по существу. Любое правдоподобное излучение для частичных классов ограничит инициализаторы свойств и код конструктора ровно одним объявлением, и это будет препятствием для настроек .Designer.cs -стилей, поскольку и пользовательский код, и сгенерированный код, вероятно, потребуют инициализаторов или логики конструктора. .
  • В JS доступно множество других композиционных механизмов - миксины, Object.assign и т. Д. Сценарий, который привел к этой функции в C #, был обусловлен ограничениями, которых просто нет в JS.
  • Текущий обходной путь interface + prototype.method = ... действительно включает сценарий сгенерированного кода так же хорошо, как partial class . Странно писать этот код вручную, но нет проблем с генерацией кода по-другому, поэтому, если вы действительно думаете, что настройка кода, созданного с помощью разделенного файла, является единственным способом решить вашу проблему (я скептически отношусь к этому), тогда этот метод доступен для вас. .
  • Мы считаем, что ES6 является арбитром «must have» для классов. В TypeScript нет ничего особенного (кроме его привлекательности для программистов на C #), из-за чего ему нужны частичные классы не больше, чем нетипизированный JavaScript. Так что, если есть какой-то сценарий, который действительно выбивает его из парка для добавления частичных классов, тогда этот сценарий должен иметь возможность оправдать себя через процесс TC39. Если это набирает обороты, мы были бы счастливы взвесить свой вклад, но это даже не кажется чьим-либо вниманием.

@RyanCavanaugh об этом:

Текущий обходной путь interface + prototype.method = ... действительно включает сценарий сгенерированного кода так же хорошо, как и частичный класс.

Как я уже неоднократно упоминал, меня устраивает, что существует неполный способ выполнения того, что мне нужно, не связанный с C #, и я просто прошу рабочий пример. david-driscoll был очень полезен, но опубликовал код, который не компилируется. Так что, если вы думаете, что я могу создать код класса со скалярными свойствами только для, например, имени, фамилии - как указано выше - это нормально. Мне просто нужен способ добавить коллекцию других типов в качестве свойства класса вручную, написав это. Но мне также нужен способ обработки инициализации этого набора.

@cosmoKenney вы на сайте скопировали правильный код? Во втором примере вместо того же имени класса использовались интерфейсы. Если хотите, я могу прикрепить почтовый индекс с кодом. Я просто колебался, потому что случайные файлы в Интернете могли быть плохими, и не хотел, чтобы вы подумали, что я публикую что-то вредоносное 🙂

Сожалеем об этом, но я уважаю принятые здесь дизайнерские решения. У меня есть пара вопросов, если это нормально:

TypeScript уже имеет слишком много функций класса, специфичных для TS (объявления свойств конструктора, инициализаторы членов, незавершенные декораторы, модификаторы доступа, инструменты и т. Д.) Для чего-то, что действительно предназначалось для того, чтобы быть просто «типами ES6 +». Частично это наша вина (наше первоначальное направление дизайна, как правило, было слишком ООП), а частично - ошибка ES6 (предложение класса много раз менялось пять лет назад, и в итоге у нас появились функции, которых не удалось). Добавление еще одной функции класса, специфичной для TS, - еще одна капля на спину верблюда, которой мы должны избегать, если можем.

Учитывая это, можно ли было бы провести четкое различие с точки зрения организации и кода между чистой типизацией JS и другими языковыми конструкциями, которые предлагает TS? Мне, со своей стороны, нравятся как набор текста, так и дополнительные языковые функции. Как потребитель / разработчик, меня не особо волнует тот факт, что TS добавляет то, чего нет в JS. Просто дайте мне классные вещи, если TC39 этого не хочет, мне все равно.

Если бы TS инкапсулировал эти вещи друг от друга, может быть, их было бы проще повторно использовать, разветвлять и строить на таких проектах, как SoundScript, а не заменять?

И тогда другой проект мог бы основываться на этой типизации, чтобы предоставить лучшие / другие языковые конструкции, чем предлагает JS.

Однозначно, будущее JavaScript - это модули. Частичные классы имеют смысл и хорошо работают в модели «глобального супа». Разрешение частичных классов, охватывающих несколько модулей, - это верный путь к катастрофе, поскольку порядок загрузки непредсказуем. Добавление функции, которая хорошо работает только в глобальной вселенной, - не лучшее использование времени.

Это правда? Конечно, модули будут естественной частью JavaScript, но как насчет основной исходной части кода? Я люблю namspaces, и если все сделать правильно, в нем нет супа. Я компилирую все это в один файл, и мне не нужны загрузчики модулей и почти во всех случаях не нужна асинхронная загрузка модулей. Некоторые предложения вроде https://github.com/Microsoft/TypeScript/issues/420 будут возможны и актуальны только для пространств имен. Вы хотите сказать, что такого не произойдет, потому что мы должны использовать только модули? На пути к прекращению поддержки пространств имен?

@ david-driscoll Спасибо, вы уже достаточно помогли. Поскольку @RyanCavanaugh - это тот, кто закрывает проблему и заявляет, что есть обходные пути, возможно, ему следует предоставить пример решения.

Я также вижу много неправильных представлений о том, что такое частичные классы. Это действительно простая конструкция, и все преувеличивают ее:
https://msdn.microsoft.com/en-us/library/wa80x488.aspx

Учитывая это, можно ли было бы провести четкое различие с точки зрения организации и кода между чистой типизацией JS и другими языковыми конструкциями, которые предлагает TS? Мне нравится как набор текста, так и дополнительные языковые функции. Как потребитель / разработчик, меня не особо волнует тот факт, что TS добавляет то, чего нет в JS. Просто дайте мне классные вещи, если TC39 этого не хочет, мне все равно.

Это было бы переписыванием языка и нарушением всякого кода.

Кроме того, фраза «Просто дайте мне классные вещи» фундаментально отличается от целей дизайна TypeScript . Если вам просто нужны классные вещи и вас не волнует согласование с JavaScript, вам следует выбрать другой язык, например CoffeeScript , Dart или Haxe . Конечно, могут быть причины, по которым они не так хорошо внедряются, как TypeScript ...

тот, кто продолжает закрывать проблему и заявляет, что есть обходные пути, может быть, ему следует привести пример обхода

Существует множество вариантов использования того, что решает partial . Практически любая языковая конструкция имеет множество вариантов использования, для которых она допустима, но по уважительным причинам вам нужно выбирать.

Вы продолжаете просить конкретную языковую функцию, например, «Я бы хотел зеленую машину», но не раскрываете суть проблемы, почему машина должна быть зеленой? Если вы объяснили, почему ваша машина должна быть зеленой, или открыто признать, что на самом деле вам все равно, какого цвета ваша машина, при условии, что это соответствует вашим реальным потребностям.

Я не понимаю, почему вы считаете, что Райан обязан рассказать вам, как перекрасить вашу машину, только потому, что по уважительным причинам автомобильная компания решила не производить зеленые автомобили.

(Да, и классы миксинов, добавленные в TypeScript 2.2, - хороший способ выполнить то, что, как я подозреваю, является вашей реальной потребностью)

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 - это не одно и то же.

  • Миксины - это автономные модули, которые можно добавлять к существующим классам.
  • Частичные классы могут обращаться к членам любого другого частичного класса, как если бы они были частью того же класса.

В качестве упражнения попробуйте реализовать следующее разделение кода с помощью миксинов:

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

В качестве упражнения попробуйте реализовать следующее разделение кода с помощью миксинов

Итак, вернемся к сути ... Итак, вместо того, чтобы указывать на языковую функцию, какую проблему вы пытаетесь решить с помощью своего примера? Поскольку обходной путь в TypeScript прост:

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

(и если мы Completist, но опять же , он решает использовать, не все случаи использования)

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

@ Слон-Судно

Учитывая это, можно ли было бы провести четкое различие с точки зрения организации и кода между чистой типизацией JS и другими языковыми конструкциями, которые предлагает TS?

На данный момент я не вижу способа разделить их. Теоретически можно было бы написать какой-нибудь язык, например CoffeeScript, который синтаксически преобразовался в TypeScript, и использовать этот язык более высокого уровня для добавления новых синтаксических функций, по-прежнему используя систему типов TS, но это только пока что. Лучше выбрать один из множества других языков компиляции в JS с нуля, если это ваша цель.

Вы хотите сказать, что такого не произойдет, потому что мы должны использовать только модули? На пути к прекращению поддержки пространств имен?

Пространства имен точно никуда не денутся. Это просто название шаблона, который уже широко используется, и они по-прежнему имеют много смысла в мире модулей ES6. Модули - это просто способ, которым люди все чаще организуют свой код, и у них есть много замечательных побочных преимуществ. Я просто говорю, что частичные классы почти не имеют смысла в этом мире, и было бы странно добавлять принципиально новый организационный паттерн, несовместимый с ним. Если бы мир двигался в противоположном направлении, и все говорили бы: «Глобальные скрипты с побочными эффектами при загрузке - это круто, все участвуют в prototype s друг друга», возможно, мы бы чувствовали себя иначе.

@RyanCavanaugh

Возможно, так ... Возможно, они не имеют такого большого смысла ... Но, как я уже много раз говорил, я думаю, что даже в этом потоке частичные классы предназначены для упрощения генерации кода ... Это основное преимущество, которое я вижу из них стоит функция в TS ... Я, и я должен предположить, что многие другие, но это всего лишь предположение, в настоящее время я генерирую тонны кода TS из моего кода C # ... Но тогда возникает вопрос, как безопасно расширить сгенерированный Код TS ??? На этот вопрос гораздо проще ответить с помощью частичных классов ... В настоящее время мы прибегаем к взломам, чтобы сохранить собственный код в наших сгенерированных файлах кода TS ...

Спасибо

То же самое и здесь: мы генерируем «прокси» машинописного текста для наших C # API, и мы хотели бы иметь возможность легко расширять эти объекты в машинописном тексте.

Я не специалист по JS, поэтому мне что-то не хватает, потому что я не понимаю, почему возможность разбить объявление классов в нескольких файлах TS (с одним объявлением класса только в одном файле JS после компиляции) было бы проблема для мира JS. Насколько я понимаю, он прозрачен для JS, как будто он изначально был записан одним файлом. Я имею в виду, если вы используете частичные классы или нет, вывод JS будет точно таким же, но это предотвратит стоимость расширяемости объекта JS для наших разработчиков.

@RyanCavanaugh

Еще одна вещь, о которой я хотел упомянуть, что лично я не прошу компилятор TS каким-либо образом решить дилемму упаковки, которая может возникнуть при создании модуля ES6 с использованием частичных классов в разных файлах TS ... Для меня это имеет смысл, по крайней мере, компилировать несколько файлов TS в один файл js для модуля ES6 ... Опять же, частичные классы значительно помогают генерации кода ... Они позволяют инструментам создавать большие части вашей модели с надежной расширяемостью ...

@kitsonk

Вы продолжаете просить конкретную языковую функцию, например, «Я бы хотел зеленую машину», но не раскрываете суть проблемы, почему машина должна быть зеленой? Если вы объяснили, почему ваша машина должна быть зеленой, или открыто признать, что на самом деле вам все равно, какого цвета ваша машина, при условии, что это соответствует вашим реальным потребностям.

Нет, я больше не прошу новую функцию. Снова и снова говорилось, что язык может делать то, что я хочу. Я привел примеры того, что я хочу (на C #), но мне еще предстоит увидеть в полезных примерах способ добраться туда, где мне нужно быть.

Я не понимаю, почему вы считаете, что Райан обязан рассказать вам, как перекрасить вашу машину, только потому, что по уважительным причинам автомобильная компания решила не производить зеленые автомобили.

Райан, кажется, здесь модератор и закрывает вопрос. Вот почему. Если он является знатоком языка, то, возможно, ему не следует закрывать вопрос, прежде чем попытаться понять мою потребность и отклонить этот запрос, не приведя примера того, как это сделать с функциями родного языка. Я заявил, что мне нужно решение во время компиляции. И я довольно хороший разработчик и какое-то время занимаюсь программированием на многих разных и малоизвестных языках, и мне все еще очень трудно с синтаксисом миксинов. Я не понимаю, как что-то такое сложное и трудное для понимания попало в язык, и все же все отвергают элегантность синтаксиса частичного класса, просто, кажется, потому, что он заимствован из C #.

Райан, кажется, здесь модератор и закрывает вопрос. Вот почему. Если он знаток языков, то, возможно, ему не стоит закрывать вопрос, прежде чем попытаться понять мою потребность.

🤔

@RyanCavanaugh , я знаю, что вы
С помощью наследования и именования я могу:

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

И это отлично работает для меня. Это не так красиво и заставляет мои классы, сгенерированные кодом, иметь имена, которые я не хочу использовать в коде. Но опять же это работает.

На данный момент я чувствую, что ребята, которые категорически против частичных классов, просто неправильно понимают концепцию частичных классов и думают, что частичные классы - это то, чем частичные классы не являются.

Чтобы прояснить: частичный класс - это синтаксическая конструкция, которая позволяет разделить определение одного и того же единственного класса на несколько физических файлов. Ключевые слова здесь «одинаковые» и «одиночные».

Документация TypeScript дает плохое определение частичных классов (первый абзац здесь: https://www.typescriptlang.org/docs/handbook/mixins.html ).

Пример концепции реальных частичных классов:

Файл "Point.generated.ts":

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

Файл "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);
  }
}

Они компилируются в Point.js - и имя происходит от имени класса, а не от имен файлов:

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

Миксины, когда они используются как «частичные классы» (и я твердо убежден, что этот термин в документации используется неправильно ), на самом деле представляют множественное наследование ( https://en.wikipedia.org/wiki/Multiple_inheritance ).

Итак, как мы видим, мы обсуждаем два разных инструмента: настоящие частичные классы и множественное наследование. Это два разных инструмента. И как любой инструмент - у них есть своя область применения. Другими словами: плотник может использовать молоток вместо молотка, когда молоток недоступен, но молоток - неподходящий инструмент.

ИМХО, у этой дискуссии нет будущего, и мы должны ее прекратить. Хотя я все еще не вижу технологических проблем для реальных частичных классов, команда действительно не хочет их реализовывать, по крайней мере, на данный момент.

Команда TS не хочет по возможности реализовывать специфические для TS функции. Если вы хотите обсудить больше, то вот лучшее место: https://esdiscuss.org/topic/class-syntax-enhancements

Я надеюсь, что на стороне TC39 есть репозиторий GitHub, чтобы обсудить эти вещи, а не на основе рассылки: /

@greendimka

На данный момент я чувствую, что ребята, которые категорически против частичных классов, просто неправильно понимают концепцию частичных классов и думают, что частичные классы - это то, чем частичные классы не являются.

Чтобы прояснить: частичный класс - это синтаксическая конструкция, которая позволяет разделить определение одного и того же единственного класса на несколько физических файлов. Ключевые слова здесь «одинаковые» и «одиночные».

Я не думаю, что здесь кто-то неправильно понимает определение частичных классов, я думаю, что вы, возможно, неправильно понимаете определение классов в JavaScript.

Если у вас сложилось впечатление, что классы JavaScript вообще похожи на классы практически любого другого широко используемого языка, тогда вам может показаться, что другие неправильно понимают.

Однако, как уже неоднократно говорилось в этом обсуждении, я знаю, что я сказал это явно, по крайней мере, один раз, классы JavaScript не являются декларативными конструкциями, они являются императивными.

Они не существуют до тех пор, пока не будут выполнены операторы.

Их определение зависит от мутации.

Это уже делает их хрупкими и зависимыми от порядка, когда дело доходит до наследования.

Это может продолжаться и продолжаться ...

@cosmoKenney

Я не понимаю, как что-то такое сложное и трудное для понимания попало в язык, и все же все отвергают элегантность синтаксиса частичного класса, просто, кажется, потому, что он заимствован из C #.

Я действительно не думаю, что это правильно. Все любят C #, даже программисты на Java 😛, и TypeScript не отличается от других.

@aluanhaddad

Я думаю, вы, возможно, неправильно понимаете определение классов в JavaScript ...

Для цитируемых и ниже: я говорю о TypeScript.
Как я уже сказал: я не вижу каких-либо технических проблем с реализацией реальных частичных классов в TypeScript, которые будут скомпилированы в конструкции JavaScript.

@greendimka

Для цитируемых и ниже: я говорю о TypeScript.
Как я уже сказал: я не вижу каких-либо технических проблем с реализацией реальных частичных классов в TypeScript, которые будут скомпилированы в конструкции JavaScript.

Проблема с этим насыщением: нет такой вещи, как класс TypeScript.

О, парень! Некоторые люди будут использовать что угодно в качестве причины.
Хорошо, в TypeScript нет классов. Или классы TypeScript. Или как там. Что бы ни.

Я имею в виду, что это классы JavaScript. Хотя иногда я виноват в своей педантичности, я очень конкретно делаю это различие, потому что нам нужно согласовать определение термина «класс», прежде чем мы сможем обсудить, какие функции легко добавить к ним.

Думаю, мы не имеем в виду одно и то же, когда говорим о занятиях, а это вызывает когнитивный диссонанс и препятствует взаимопониманию.

Думаю, мы не имеем в виду одно и то же, когда говорим о занятиях, а это вызывает когнитивный диссонанс и препятствует взаимопониманию ...
Полностью согласен!

На самом деле первый пост в этой теме прекрасно описывает то, что было запрошено, поэтому у этого обсуждения нет будущего :)

@greendimka да, у него нет будущего, поскольку кажется, что у TypeScript никогда не будет частичных классов. На мой взгляд, это хорошо.

Однако недопонимание и недопонимание - это нехорошо, поэтому я все еще разговариваю с вами.

К сожалению, вы, похоже, тоже не интересуетесь

A. Объяснение того, что такое классы JavaScript, состоит в том, чтобы можно было передумать и согласиться с вами, что их можно тривиально сделать частичными.

Б. Узнавать у других о том, что такое классы JavaScript, чтобы понять противоположную точку зрения.

Я считаю, что это обидно, но я не буду обсуждать это дальше, если вы того пожелаете.

Я не против обсуждений. Но здесь несколько раз говорилось, что частичные классы не будут реализованы (по крайней мере, сейчас). Поэтому я считаю, что лучше сосредоточиться на других вещах. Может быть, TypeScript можно будет изменить в будущем, кто знает.
Что касается A и B. Я ненавижу JavaScript. Но я это очень хорошо знаю и, конечно, знаю, как в нем «представлены» классы. Но цель всего этого обсуждения состояла не в том, чтобы модифицировать JavaScript, а в том, чтобы улучшить возможности TypeScript, как более высокого языка, который производит «низкоуровневый» код JavaScript.
Представьте, что мы заменим TypeScript на C ++ и JavaScript на машинный код. В машинном коде отсутствует большинство концепций, существующих в C ++. Но должен ли C ++ перестать развиваться из-за этого? Конечно нет. Должен ли он перестать развиваться, потому что некоторые старые компиляторы не смогут скомпилировать? Конечно, нет - вы даете разработчикам новую функцию и говорите им: она работает с новыми компиляторами - вы (разработчики) решаете, хотите ли вы ее использовать.

@aluanhaddad
Блин, блин. Кого волнует часть уравнения, связанная с Javascript? TypeScript - это уровень выше, или как вы хотите его сформулировать. Помните старые дискуссии о сравнении 4GL и 3GL? TypeScript относится к Javascript так же, как 4GL относится к 3GL. Также аргумент, что TypeScript - это ES6 со строгими типами, таким образом, частичные классы выходят за рамки дорожной карты TypeScript - LAME. У нас есть миксины, универсальные шаблоны, модули, пространства имен и приведение типов. Так почему бы не пройти лишнюю милю с неполными классами?

Все, что мы хотим от частичных классов, - это синтаксический сахар, который позволяет нам объединить все различные определения одного класса TypeScript в одно окончательное - низкоуровневое - 3GL - определение класса Javascript. Это не должно повлиять на окончательное определение класса JavaScript, верно? Почему конечный продукт преобразования в Javascript даже является частью этого обсуждения? Шутки в сторону.

@cosmoKenney «это просто ES.next с типами» - это большой аргумент, который привлекает разработчиков JavaScript. Если вы хотите чего-то большего, вы смотрите не на тот язык. Может, лучше попробовать

edit: У меня только что было интересное осознание того, что в ES.next частичные классы могут быть реализованы с помощью пользовательского загрузчика модулей. Если вы используете

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

загрузчик может объединить все экспортированные определения MyClass из любых файлов, соответствующих подстановочному знаку, в один класс, а затем предоставить это.

@spion Мне нравится, как вы совершенно правильно называете их частичными классами ES.next. Загрузчик модуля ES, такой как браузер, или полифил загрузчика, такой как SystemJS, будет тогда поддерживать частичные классы.
Одна из основных проблем с добавлением этой функции на уровне TypeScript заключается в том, что она нарушает существующие инструменты, такие как загрузчики и упаковщики. С другой стороны, если бы они были указаны в ECMAScript, то все эти инструменты реализовали бы эту функцию, и TypeScript оставался бы совместимым со всеми этими инструментами.

Я определенно согласен с вами относительно Scala.js

@cosmoKenney

У нас есть миксины, универсальные шаблоны, модули, пространства имен и приведение типов. Так почему бы не пройти лишнюю милю с неполными классами?

Примеси, представленные в TypeScript, являются шаблоном проектирования ECMAScript, который TypeScript _types_.

Обобщения - это функция системы типов, поэтому они не применяются.

Модули - это функция ECMAScript.

Пространства имен являются синтаксическим сахаром и формализацией шаблона проектирования ECMAScript.

Приведения типов не существует в TypeScript.

@aluanhaddad, почему ты продолжаешь делать это во время выполнения? Загрузчики модулей и все, что не имеет отношения к транспилированию.

+1

@cosmoKenney

@aluanhaddad, почему ты продолжаешь делать это во время выполнения? Загрузчики модулей и все, что не имеет отношения к транспилированию.

Это не так.

Посмотрите на экосистемы SystemJS и Webpack, и вы убедитесь в обратном.

Даже более традиционные и надежные рабочие процессы Gulp полагаются на соответствие между входными и выходными файлами.

+1

Мне нужен частичный класс, потому что мы генерируем большую часть базового класса с помощью инструментов (для автоматизации, когда есть изменения в модели). Затем мы используем частичный класс, чтобы добавить функциональность к классу в отдельных файлах, чтобы он не был перезаписан инструментом (который автоматически генерирует класс).

Была ли эта страница полезной?
0 / 5 - 0 рейтинги