Typescript: Разрешить видимость конструкторов

Созданный на 13 мар. 2015  ·  42Комментарии  ·  Источник: microsoft/TypeScript

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

Fixed Suggestion help wanted

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

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

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

Поскольку все «классы» на самом деле являются просто функциями и поскольку не существует такой вещи, как невызываемая функция, везде, где виден класс, вы также можете использовать для него оператор new .

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

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

@billccn Я не люблю убивать запросы,
Дело в том, что полностью защищено в TS и сгенерированном JS, другое дело - защитить это до точки генерации JS. Как вы объяснили, полная защита невозможна, но должна быть возможна проверка компилятором другой видимости.
Если вам не нравятся модификаторы видимости, везде используйте общедоступные по умолчанию, есть и другие, которые считают эту концепцию полезной.

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

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

Я думаю, что только проверка компилятора - это выход.

Принято, принимаю PR

Чтобы уточнить, может ли класс с частным конструктором быть расширяемым?

т.е. будет ли это вызывать ошибку?

class A {
    private constructor() {
    }
}

class B extends A { // Should there be an error at A saying: "Cannot extend private class 'A'"?
}

если да, то позволим ли мы это:

class A {
    protected constructor(a?: any)
    private constructor() {

    }
}

class B extends A { // No error since 'A' has a non-private constructor
}

Исходя из опыта разработчиков, не связанных с JS, это ожидаемое поведение.

В первом примере B должно быть ошибкой, потому что его неявный супервызов недопустим. Таким образом, класс с private constructor фактически равен sealed / final .

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

См. Также # 471. Действительно ли требуется, чтобы конструкторы были частными или будут защищены?

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

Смотрите здесь .

@dsherret protected отвечает всем этим требованиям.

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

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

"2. Убедитесь, что класс не может быть расширен. Это не позволяет неосторожным или вредоносным подклассам нарушить неизменяемое поведение класса, ведя себя так, как если бы состояние объекта изменилось. Предотвращение создания подклассов обычно достигается путем создания класса final, но есть это альтернатива, которую мы обсудим позже.

В настоящее время в TypeScript нет поддержки final / sealed , поэтому закрытый конструктор - единственный способ создать неизменяемый класс с точки зрения системы типов. (Хотя я рекомендую также заморозить объект в конструкторе.)

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

Но вместо статического языка, такого как Java, более подходящим для сравнения был бы Perl, другой динамический язык: http://www.perlmonks.org/?node_id=437623

Одна из причин, по которой perl не имеет модификаторов доступа, таких как общедоступный, частный и защищенный, заключается в том, что признано, что они часто мешают выполнению работы: то, что было задумано исходным дизайнером, не имеет ничего общего с что вы хотите с этим делать. Точно так же дизайн для гибкости - хотя вы, возможно, сейчас не видите, что он вам нужен, следующий человек, который придет, может увидеть, что это невероятно удобно для решения этой новой проблемы, и благословит ваш гений за развитие этой гибкости ;-)

и:

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

http://www.perlmonks.org/?node_id=1096925

Фактически, JavaScript - то же самое, и - да - TypeScript - то же почти во всех отношениях. В машинописном тексте вы можете легко получить доступ к закрытым членам - используя метко названный escape-штрих: obj["propertyName"] .

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

Я не понимаю дискуссию о «достаточности защищенности». Если TS имеет концепцию видимости, и я могу применить эту концепцию к конструкторам, ответ - «этого недостаточно».

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

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

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

class Currency {
    private constructor(private value: number, private type: CurrencyType) {
    }

    static fromNumberAndType(value: number, type: CurrencyType) {
        return new Currency(value, type);
    }

    static fromString(str: string) {
        const value = ...,
              type  = ...;

        return new Currency(value, type);
    }

    // ... omitted ...
}

// error:
const badCurrency = new Currency(5.66, CurrencyType.USD);
// ok:
const goodCurrency1 = Currency.fromNumberAndType(5.66, CurrencyType.USD);
const goodCurrency2 = Currency.fromString("5.66 USD");

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

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

@benliddicott То же
Используйте TS и создайте некоторые правила вроде линта, которые запрещают использование private в конструкторе. Перефразируя ваш последний комментарий: «Это проблема с инструментами, а не с языковым дизайном».

@benliddicott, если что-то сделать невозможно, мне не придется отправлять его обратно после проверки кода. Это экономит время.

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

@dsherret Нет, это произвольное ограничение, которое дает Architecture astronauts : -1:

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

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

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

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

@dsherret Недостаточно для документирования, а не для

Это значительно усложняет множественное наследование, см. # 4805 (что сейчас кажется мне запоздалой мыслью). Я уже выразил некоторые из своих мыслей в # 3578, так что не буду беспокоиться об этом снова. Получилось немного круто с astronaut , не хочу никого обидеть.

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

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

Вот еще один способ написать свой пример:

module Currency {
    export enum Type {
        CAD = 1.3,
        USD = 1,
    }

    class PrivateCurrency {
        constructor(private value: number, private type: Type) { }
    }

    export function fromNumberAndType(value: number, type: Type) {
        return new PrivateCurrency(value, type);
    }

    export function fromString(str: string) {
        let value = 10;
        let type = Type.CAD;
        return new PrivateCurrency (value, type);
    }
}

Меньше OO-иш, но у вас действительно есть «действительно» настоящий частный класс.

@jbondc Замечательно, что вы нашли свой путь к частным занятиям! Пусть другие используют другой подход (экспортируемый класс с частным конструктором). Теперь вы можете заметить, что могут быть функции, которые удовлетворяют потребности группы A и не нарушают работу группы B. :)

+1

+1

Это все еще на радаре? PR чертовски устаревший!

+1

+1

+1

: +1: может быть полезно для заводских шаблонов проектирования

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

interface ExampleBuilder {
    Instance(): Example;
}

export interface Example {
    Val(): number;
}

export let Example: ExampleBuilder = class ExampleImpl {
    constructor(v: number) {
    }

    Val(): number {
        return 42;
    }

    static Instance(): Example {
        return new ExampleImpl(2);
    }
};

let x = Example.Instance(); // OK: x has type Example
let y = new Example(5);     // ERROR: Cannot use 'new' with an expression whose type lacks a call or construct signature.

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

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

Лично я просто помечаю все свои будущие частные конструкторы комментариями // todo: make private once supported и не вызываю конструкторы. Было бы неплохо, когда эта функция появится, чтобы получить некоторую правовую поддержку и лучшую документацию с модификатором доступа.

@dsherret

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

Согласовано. Мы не слишком страдаем от этой когнитивной нагрузки, потому что эти классы генерируются кодом. Так что интерфейс для программиста на самом деле приятный и простой.

исправлено https://github.com/Microsoft/TypeScript/pull/6885

спасибо @AbubakerB!

Привет, эта функция будет выпущена в какой-нибудь будущей версии машинописного текста?
На данный момент попытка объявить частный или защищенный конструктор дает мне эту ошибку в машинописном тексте 1.8.10:
ошибка TS1089: модификатор private не может отображаться в объявлении конструктора.

Ах, забудь. Только что узнал о дорожной карте, в которой говорится, что эта функция будет включена в Машинопись 2.0.

Ах, забудь. Только что узнал о дорожной карте, в которой говорится, что эта функция будет включена в Машинопись 2.0.

Также отметка TypeScript 2.0 и метка Fixed будут указывать на то, что она включена. Все, что помечено как Fixed обычно включается в мастер и доступно через npm install typescript@next .

Я использую TypeScript 2.0.2 RC и все еще получаю TS1089, когда пытаюсь создать конструктор private . Я делаю это неправильно или это просто не исправлено?

Я использую TypeScript 2.0.2 RC и все еще получаю TS1089, когда пытаюсь создать частный конструктор. Я делаю это неправильно или это просто не исправлено?

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

Я нашел проблему. Это была ошибка gulp-typescript которая использовала неправильную версию tsc несмотря на мои спецификации в package.json и то, что было разрешено в PATH .

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

  1. require TypeScript перед gulp-typescript следующим образом:
// Tool-Chain: Scripts
var tsc = require("typescript");
var typescript = require('gulp-typescript');
  1. При определении моей задачи сборки укажите компилятор в качестве замены:
// Task(s): Build TypeScript Outputs
var tsconfig = typescript.createProject("path to tsconfig", { typescript: tsc });
gulp.task('build:scripts', function () {
    let ts = tsconfig.src()
                     .pipe(sourcemaps.init())
                     .pipe(typescript(tsconfig));

    return ts.js.pipe(sourcemaps.write(".")).pipe(gulp.dest(path.join(outputs.root, outputs.scripts)))
});
Была ли эта страница полезной?
0 / 5 - 0 рейтинги