Typescript: Поддержка предлагаемых свойств ES Rest/Spread

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

Предложение es7: https://github.com/sebmarkbage/ecmascript-rest-spread

Свойства спреда

Ввод

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

var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3, y: 4}; // not an error

У меня есть очень наивный алгоритм проверки типа для аналогичной функции ( JSXSpreadAttribute ) в моем маленьком форке jsx-typescript : я просто копирую свойства _spread object_ в таблицу свойств, когда сталкиваюсь с распространяемым объектом и переопределить это свойство, если я встречу объявление с похожим именем.

Излучающий

jstransform использует Object.assign , babel вводит прокладку:

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

Мы могли бы либо форсировать наличие функции assign на интерфейсе ObjectConstructor , либо предоставить аналогичную функцию (с другим именем).

Я думаю, что оптимальным решением было бы не генерировать вспомогательную функцию в es6 target (или если Object.assign определено) и генерировать вспомогательную функцию для es5 , es3 .

var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3};

/// ES6 emit
var obj = {x: 1, y: 2};
var obj1= Object.assign({}, obj, { z: 3 });

//ES3 emit
var __assign = function (target) { 
    for (var i = 1; i < arguments.length; i++) { 
        var source = arguments[i]; 
        for (var key in source) { 
            if (Object.prototype.hasOwnProperty.call(source, key)) { 
                target[key] = source[key];
            } 
        } 
    } 
    return target; 
};

var obj = {x: 1, y: 2};
var obj1= __assign({}, obj, { z: 3 });

Остальные свойства

Ввод

Для простого объекта новый тип является подтипом назначения, который не содержит свойств, которые были захвачены перед остальными свойствами:

var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;
obj1// {x: number; y:number};

Если присваивание деструктурирования имеет объявление индекса, результат также имеет аналогичное объявление индекса:

var obj: { [string: string]: string };
var {[excludedId], ...obj1} = obj;
obj1// { [string: string]: string };

объявления new/call явно не захвачены:

var obj: { (): void; property: string};
var { ...obj1} = obj;
obj1// { property: string };

Излучающий

Невозможно создать _rest properties_ без вспомогательной функции, это из Babel:

var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;
var __objectWithoutProperties = function(obj, keys) {
    var target = {};
    for (var i in obj) {
        if (keys.indexOf(i) >= 0) continue;
        if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
        target[i] = obj[i];
    }
    return target;
};

var obj = {x:1, y: 1, z: 1};
var z = obj.z;
var obj1 = __objectWithoutProperties(obj, ["z"]);

_Edit: добавлен небольшой пример ввода/вывода_

Committed ES Next Fixed Suggestion

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

Я не уверен, как мы в конечном итоге выпустим его, но я начал работу над rest/spread в литералах объектов и деструктурированием . Прямо сейчас мы планируем использовать наш полифилл __assign для эммита.

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

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

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

Недавно я обнаружил эту функцию в ES7 благодаря этой статье . Надо сказать, это очень удобно!

Хотелось бы увидеть это в TypeScript!

Есть обновления по этому поводу? Хотелось бы увидеть это в 1.6, так как это будет очень удобно при использовании с React :smile:

@prabirshrestha для протокола: в настоящее время у нас есть оператор распространения свойств для JSX на master .

@DanielRosenwasser Не могли бы вы уточнить? Означает ли это, что это работает на мастере с флагом --jsx? Включает ли это остальные свойства или только свойства распространения ?

Извините, @mnpenner и @prabirshrestha , да, я имел в виду на master . @RyanCavanaugh знает об этом больше, чем я, но я считаю, что это просто свойства распространения.

JSX поддерживает распространение _attributes_, например <TagName {...spreadedExpr} /> . Ничего общего с оператором распространения ES6, кроме совместного использования одного и того же токена и примерно эквивалентной семантики.

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

Rest/Spread теперь одобрен для Этапа 2 https://github.com/tc39/tc39-notes/blob/master/es7/2015-07/july-30.md

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

Это очень полезно в React и Redux, пожалуйста, внедрите его как можно скорее, пожалуйста :-)

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

https://gist.github.com/tomduncalf/fbae862b123445c117cb

Это то, за что вы бы приняли патч? (будет объединен, как только он достигнет этапа 3). И если да, есть ли какие-нибудь указания, с чего начать вносить изменения для его поддержки?

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

Мы бы рассмотрели PR здесь. Одно замечание: подключение системы типов для этой функции — непростая задача. Если вы заинтересованы в просмотре этого, я бы оставил его инкрементным и начал бы с синтаксического анализа, а затем испустил бы, а затем проверил тип. @sandersn сможет помочь вам, если у вас возникнут вопросы.

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

В любом случае :+1: для операторов спреда в ТС. :улыбка:

@xogeny Мне было бы очень интересно взглянуть на эту библиотеку. Спасибо

@ cur3n4 У меня есть репозиторий, в котором я играю с этими идеями. Он немного меняется (поскольку это все еще развивается). Я использовал специальные модули, которые изначально использовали updeep и эта функциональность все еще находится в библиотеке . Но я перестал его использовать, потому что мне было трудно поддерживать необходимые ограничения типов. Я подозреваю, что если бы в Typescript было что-то вроде общих ограничений типов Java super , updeep (и несколько других библиотек, таких как lodash или ramda ) могли бы быть сделал намного больше безопасных типов.

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

После использования Babel в течение последних нескольких месяцев я преобразовал свой проект в TS, и мне пришлось все это переделывать. Хотелось бы увидеть это реализованным!

: +1:

хотелось бы увидеть это.

+1

должна иметь функцию, пожалуйста, реализуйте

+1

+1 гораздо более приятный синтаксис, чем Object.assign + поддержка JSX

+1 это необходимо для разработки реакции для передачи реквизита вниз ( var { checked, ...other } = this.props; . см. Реагирующую документацию

это необходимо для реактивной разработки

Давайте будем честными, это удобство, а не необходимость. Предложение находится на этапе 2 с TC39. Команда TypeScript ясно дала понять, что они не хотят рассматривать реализацию вещей, не созданных в TypeScript, до тех пор, пока они не достигнут стадии 3. Место для «+1» — это TC39.

@kitsonk - вы не можете ожидать хорошего принятия, когда такие фреймворки, как react / и т. д., специально

Ну, я думаю, мы говорим о более широкой теме. Ни один поставщик браузера не будет реализовывать что-либо, что не находится на стадии 3 (если только это не нравится им лично). TC39 пытались придать определенный уровень организации своей части Интернета. TypeScript сильно пострадал из-за спешки раньше (модули, и посмотрите на хаос, который это вызвало с тех пор). Я уважаю то, что они пытаются, по крайней мере, дать некоторое руководство и структуру вокруг того, что они готовы реализовать и когда. Есть ли другое предложение о том, какой стандарт следует использовать? Я не думаю, что «потому что X framework упоминает об этом в своих документах» будет хорошим вариантом.

такие фреймворки, как react/etc, специально указывают такие вещи в своих документах

Который они помечают как просто предложение, а затем продолжают инструктировать вас, как прыгать через обручи, чтобы настроить Babel 6 для его использования. Я бы скорее обвинил фреймворки в том, что они зависят от синтаксических технологий, которые находятся только в черновой спецификации. Лично мы в Додзе обожглись на Object.observe и я снова воздерживаюсь от попыток полагаться на технологии до того, как они дойдут до Стадии 3 в Додзё.

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

В целом, я думаю, что TS довольно хорошо представлен сегодня. Было время (я думаю, около 1,4 ~ 1,5), когда я был разочарован отсутствием некоторых функций ES2015, но на сегодняшний день TS довольно хорошо догнал стандарт.

Конечно, иногда есть зависть к другим языкам. Например, Babel предлагает практически любые предложения JS, включая «экспериментальные». Лично я с нетерпением жду этого выпуска и оператора привязки функции.

Но в то же время мы должны признать, что команда TS гораздо серьезнее относится к совместимости, чем Babel. Они не хотят менять семантику вашего кода между выпусками или просто удалять функцию, что и делает Babel. Для некоторых (корпоративных?) проектов это важно.

Я также могу понять, что MS не хочет вкладывать много ресурсов в функции, которые могут быть удалены, на ум приходит фиаско Object.observe . На самом деле, они сказали, что рассмотрят возможность пиара на этот пример : wink: .

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

Просто краткое примечание здесь. Мы довольно медленно внедряли предложенные на этапах 0-1 функции ES в основном из-за проблем с обратной совместимостью. Мы внимательно следим за дискуссиями на собраниях TC39 и непосредственно участвуем в собраниях или в подготовке предложений перед собраниями; и когда мы добавляем функцию, мы хотели бы знать, что это значит для пользователей, которые зависят от них.
Если вы посмотрите на документацию процесса TC39 , функция на этапе 1 может ожидать «основных» изменений после принятия. Это касалось классов, модулей, итераторов и почти всех нетривиальных функций ES6.
Надев мою пользовательскую шляпу, критические изменения в синтаксисе могут быть механическими, в то время как критические изменения в семантике может быть чрезвычайно трудно поймать и исправить; это может дорого обойтись командам, использующим любую из этих функций, и/или блокировать внедрение новых версий инструментов. и мы хотели бы минимизировать это, когда это возможно. Вот почему мы приняли политику включения функций по умолчанию, когда они достигают стадии 3, а до этого — под экспериментальным флагом (например, декораторы).
Сказав это, распространение и остальная часть свойства объекта находятся в нашей дорожной карте , и мы планируем заняться ими в будущем выпуске.

Может быть, стоит подумать о том, чтобы добавить к компилятору возможность вводить некоторые сторонние расширения, как это делается в babel?

Еще одна мысль: как насчет поддержки предварительной обработки исходников машинописного текста (с помощью Babel)?

Если мы сможем заставить Babel (или любую другую библиотеку) расширять распространение объектов в вызовы Object.assign , это может работать достаточно хорошо? Мы надеемся, что это можно будет обобщить и на другие экспериментальные функции, реализованные в Babel.

@kitsonk - абсолютно широкая тема,

. TypeScript сильно пострадал из-за спешки раньше (модули, и посмотрите на хаос, который это вызвало с тех пор).

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

дать некоторое руководство и структуру вокруг того, что они готовы реализовать и когда

они реализуют декораторы (этап 1), свойства класса (этап 1) и т. д.

Я не думаю, что «потому что X framework упоминает об этом в своих документах» будет хорошим вариантом.

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

Я думаю, что TypeScript во многом догоняет Babel, и у него есть много проблем, таких как: очень сложно начать использовать его с существующим приложением, переход с Babel на машинописный текст для A2 — это КОШМАР!, отсутствие несвязанных плагинов делает работу с Node чрезвычайно сложной.

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

Я очень уважаю React, но я не шучу. Это не единственная игра в городе, и она, безусловно, находится на большой кривой ажиотажа. Полгода назад это был React + Flux, а теперь React + Redux — это вкус дня. Год назад Angular и Polymer. Два года назад это были Backbone и Ember. Шесть лет назад это были Dojo, MooTools, jQuery, gwt, ExtJS и т. д.

не начинайте здесь войну фреймворков :exclamation:
Синтаксис {..., } и так очень красив, независимо от фреймворка

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

@amcdnl Что именно вы имеете в виду? Я могу только предположить, что вы имеете в виду модуль как проблему пространства имен, которая не имеет ничего общего с .NET, а также не является одной из основных проблем с модулями в TypeScript.

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

пусть машинописный текст = { ... машинописный текст, object_spread }; // Да, пожалуйста.

Облом, в этом нет никакого движения, я очень надеюсь, что это скоро достигнет стадии 3. Будет убивать подсветку синтаксических ошибок в VSCode, если это возможно

Интересно, меняет ли Сальса что-нибудь в игре?

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

Но теперь, когда Salsa является JS-движком VS Code по умолчанию, давление на более новый синтаксис JS (по крайней мере, только синтаксис, а не полный набор TS) будет увеличиваться. Особенно с учетом популярности Бабеля.

Сможет ли Salsa принять более широкий синтаксис быстрее, чем TS?

+1

Это будет в 2.1, а не в 2.0? :плакать:
https://github.com/Microsoft/TypeScript/wiki/Дорожная карта#21

Учитывая, насколько большим уже оказывается 2.0, я не слишком удивлен.

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

Так это исправлено?

...автономная вспомогательная функция для атрибутов спреда
https://github.com/Microsoft/TypeScript/releases/tag/v1.8.10

Поскольку я все еще получаю «Ожидается шаблон реструктуризации собственности»

Не совсем. Это исправление предназначено специально для атрибутов распространения JSX:

const props = { foo: "bar" };
return <SomeComponent {...props} />;

который уже работал, до того, как React v15 удалил недокументированную внутреннюю функцию, от которой зависел JSX-компилятор TypeScript. Ой 😃

Распространение объектов — это другое предложение с, по общему признанию, похожим синтаксисом (возможно, оно даже было предложено инженерами Facebook и вдохновлено эквивалентом JSX, хотя я точно не знаю).

Итак, поскольку {...props} еще не работает, как бы вы с помощью TypeScript достигли чего-то подобного?

Итак, поскольку {... props} еще не работает, как бы вы, используя TypeScript, достигли чего-то подобного этому

Используйте xtend : https://www.npmjs.com/package/xtend, у него отличная типизация: https://github.com/typed-typings/npm-xtend (от @blakeembrey : rose:) спасибо к типу перекрестка

Или вообще не используйте никакую библиотеку!

const newProps = Object.assign({} /*new object*/, props /* add all attributes of props */, {
   // add additional props
  bar: "baz"
});

Это немного многословно, но _является_ родным для ES2015, поэтому, если у вас уже есть полифилл для этого, вы надежны.

Этот оператор — одна из основных причин, по которой мы решили использовать в проекте babel вместо typescript.

Полностью меняет игру для неизменной обработки объектов.

В качестве альтернативы планируется ли что-нибудь, что позволит вам подключить пользовательское преобразование к TSC, как вы можете сделать в Babel? Может быть, даже на основе того же API, это было бы здорово и решило бы такие проблемы.

В качестве альтернативы планируется ли что-нибудь, что позволит вам подключить пользовательское преобразование к TSC, как вы можете сделать в Babel?

@Ниондир Да. Просто найдите теги [Transforms] . Они необходимы для правильной транспиляции async/await/generator в компилятор TypeScript :rose:

После небольшого поиска, вы ссылаетесь на это? https://github.com/Microsoft/TypeScript/wiki/Использование API-интерфейса компилятора

Звучит здорово. Я просто не нашел ничего, связанного с распространением объектов для этого API. Возможно, было бы неплохо решить эту проблему на основе Compiler API :)

@Niondir извините за то, что не предоставил лучшую помощь в моем исходном сообщении. Я говорю о https://github.com/Microsoft/TypeScript/issues/5595 <, который позволит использовать подключаемый emit для синтаксиса ESNext. TypeScript немного более сложен, чем Babel, потому что он должен иметь _семантическое_ понимание системы типов вашего кода, поэтому работа по распространению объектов, вероятно, должна исходить от команды TypeScript.

Наличие эмиттера на основе плагина облегчило бы эту задачу. Да, вы бы использовали API-интерфейс компилятора (или, возможно, fork компилятор в начале, пока система плагинов не станет общедоступной + для логики семантики / сканера)

Это может быть вопрос для другой проблемы, но нельзя ли добавить параметр «разрешить-экспериментальный» и разрешить использование TypeScript в будущем? Например, разрешите оператор распространения и вывод как есть. Я могу справиться с этим с помощью Babel впоследствии.

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

Я не уверен, как мы в конечном итоге выпустим его, но я начал работу над rest/spread в литералах объектов и деструктурированием . Прямо сейчас мы планируем использовать наш полифилл __assign для эммита.

Это лучшая новость, которую я слышал за последние 3 месяца!

К сожалению, оказалось, что заставить это работать с дженериками — _много_ работы. Вы должны правильно поддерживать такой код:

function addId<T>(t: T): {...T, id: number} {
    return { ...t, id: 1 };
}

Как видите, addId возвращает тип нового типа, тип объекта, который включает оператор расширения.

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

Простите мое невежество ... но похоже, что addId в этом случае сможет вернуть T & { id: number } ? Или есть какая-то особенность типов союзов, которая мешает этому быть оптимальным решением?

Я думаю, он имел в виду, что на самом деле нужно проверить T , так как вы можете передать что угодно T

addId<boolean>(true);
addId<number>(5);

Babel молча игнорирует такие утверждения let a = { ...true } но я думаю, что они не должны быть действительными.

Это не T & { id: number } потому что если T имеет свойство id , оно будет переопределено.

С другой стороны, пересечение, результирующий тип id будет пересечением типа T id и number .

Итак, @sandersn говорит, что вам нужен отдельный оператор типа.

Хотя мы определенно выиграем от лучшего решения для ввода здесь, Object.assign уже существует и наивно использует пересечения в качестве типа вывода. Почему бы нам не использовать в качестве временного интервала оператор rest, делающий именно то, что делает Object.assign ?

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

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

Как только мы получим это веселее. Будет отлично работать с реакцией!

Я был бы счастлив, даже если бы это не было напечатано

@ 2426021684 Это может звучать разумно, но any , любое использование синтаксиса будет просачиваться any в листья выражений.

@aluanhaddad это будет то же самое, что и Object.assign который является обходным путем, который приходится использовать всем, потому что у них нет выбора (см. пост JabX выше)

Пожалуйста, рассмотрите возможность реализации предложения @JabX в ближайшее время. Это предложение является этапом 3 во всем, кроме названия, оно _определенно_ будет частью стандарта и имеет очень ясную и простую семантику. Внедрение синтаксической поддержки спредов свойств + наивная типизация Object.assign было бы очень полезным временным решением, пока мы ждем «правильной» реализации. Не позволяйте совершенству быть врагом хорошего.

Остальные свойства чрезвычайно важны для React 15.2, как можно увидеть здесь https://facebook.github.io/react/warnings/unknown-prop.html.

const divProps = Object.assign({}, props)
delete divProps.layout

очень некрасиво, особенно если количество реквизита на компоненте выше

для тех, кто не может больше ждать, вот обходной путь:

function steal(result: any, data: any): any {
    for (var key in data) {
        if (value.hasOwnProperty(key)) {
            result[key] = data[key];
        }
    }
    return result;
}

export class SameAs<a> {
    constructor(public result: a) { }
    public and<b>(value: b): SameAs<a & b> {
        return new SameAs<a & b>(steal(this.result, value));
    }
}
export function sameAs<a>(value: a): SameAs<a> {
    return new SameAs(steal({}, value));
}

// example of use:

const mixture = sameAs(one).and(anotherOne).and(yetAnotherOne).result; // <-- ta-da!

Игнорируйте этот пост, смотрите ниже для лучшей реализации

Вот что я придумал для операции деструктурирования бедного (машинописного) кодера:

declare interface ObjectConstructor {
  destruct<T extends Object>(data: T, props: any): T;
  destruct<T extends Object>(data: T, ...propNames: string[]): T;
}

function destruct<T extends Object>(data: T, ...removals: string[]) {
  const rest = <T>{};

  const keys = removals.length === 1 && typeof removals[0] === 'object' ?
    Object.getOwnPropertyNames(removals[0]) :
    <string[]>removals;

  Object
    .getOwnPropertyNames(data)
    .filter(x => keys.indexOf(x) < 0)
    .forEach(x => {
      (<any>rest)[x] = (<any>data)[x];
    });

  return rest;
}

Object.destruct = destruct;

// Usage example:

const srcObj = { A: 'a', B: 'b', C: 'c' };
// destruct using an object template
const onlyC = Object.destruct(srcObj, { A: null, B: null });
// destruct using property names
const onlyA = Object.destruct(srcObj, 'B', 'C');

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

Обновление (также игнорируйте это, смотрите ниже для еще лучшего)

Я переработал это (после того, как возился с ним в реальной среде) и придумал гораздо более простые для работы функции.

function deconstruct<TResult, TData>(
  result: TResult,
  data: TData,
  onHit: (result: TResult, data: TData, x: string) => void,
  onMiss: (result: TResult, data: TData, x: string) => void,
  propNames: string[]
  ) {

  Object
    .getOwnPropertyNames(data)
    .forEach(x => {
      if (propNames.indexOf(x) < 0) {
        if (onMiss != null) {
          onMiss(result, data, x);
        }
      }
      else {
        if (onHit != null) {
          onHit(result, data, x);
        }
      }
    });

  return result;
}

// shallow clone data and create a destructuring array of objects
// i.e., const [ myProps, rest] = destruct(obj, 'propA', 'propB');
function destruct<T>(data: T, ...propNames: string[]) {
  return deconstruct(
    [ <T>{}, <T>{} ],
    data,
    (r, d, x) => (<any>r[0])[x] = (<any>d)[x],
    (r, d, x) => (<any>r[1])[x] = (<any>d)[x],
    propNames
  );
}

// shallow clone data and create a destructuring array of properties
// i.e., const [ propA, propB, rest] = destructProps(obj, 'propA', 'propB');
function destructProps(data: any, ...propNames: string[]) {
  return deconstruct(
    [ <any>{} ],
    data,
    (r, d, x) => r.splice(r.length - 1, 0, d[x]),
    (r, d, x) => r[r.length - 1][x] = d[x],
    propNames
  );
}

// shallow clone data and remove provided props
// i.e., const excluded = omit(obj, 'excludeA', 'excludeB');
function omit<T>(data: T, ...propNames: string[]) {
  return deconstruct(
    <T>{},
    data,
    null,
    (r, d, x) => (<any>r)[x] = (<any>d)[x],
    propNames
  );
}

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

const [ props, restProps ] = destruct(omit(this.props, 'key', 'ref'), 'id', 'text');

В этом случае props вводится так же, как this.props , но не содержит каких-либо реквизитов, предназначенных для передачи вниз по линии (которые сейчас находятся в restProps )

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

согласен, я сейчас работаю над более безопасной версией.

Еще один удар по этому:

Расширение основного объекта:

declare interface ObjectConstructor {
    rest<TData, TProps>(data: TData, propsCreator: (x: TData) => TProps, ...omits: string[]): { rest: TData, props: TProps };
}

function rest<TData, TProps>(data: TData, propsCreator: (x: TData) => TProps, ...omits: string[]) {
  const rest = <TData>{};
  const props = <TProps>propsCreator.apply(this, [ data ]);

  Object
    .getOwnPropertyNames(data)
    .filter(x => props.hasOwnProperty(x) === false && omits.indexOf(x) < 0)
    .forEach(x => {
      (<any>rest)[x] = (<any>data)[x];
    });

  return {
    rest,
    props,
  };
}

Object.rest = rest;

Специальное расширение React:

declare module React {
  interface Component<P, S> {
    restProps<T>(propsCreator: (x: P) => T, ...omits: string[]): { rest: P, props: T };
  }
}

function restProps<P, S, T>(propsCreator: (x: P) => T, ...omits: string[]) {
  return Object.rest((<React.Component<P, S>>this).props, propsCreator, ...omits.concat('key', 'ref'));
}

React.Component.prototype.restProps = restProps;

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

const src = { A: 'a', B: 'b', C: 'c' };
const { rest, props } = Object.rest(src, x => {
    const props = { A, C } = x;
  return { A, C };
});

И пример использования расширения реакции:

const { rest, props } = this.restProps(x => {
  const { header, footer } = x;
  return { header, footer };
});

Все надписи должны быть сохранены. единственная часть, которая меня беспокоит, — это создатель реквизита, потому что он требует дублирования. Думаю, я мог бы взломать его и предположить, что деструктурированный объект живет в _a но это кажется _опасным_...
_ПРИМЕЧАНИЕ _: _a даже не является жизнеспособным хаком, так как это было бы эквивалентно x .

Вот скрипка, с которой можно поиграть.

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

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

Поскольку в основном люди здесь хотят использовать это с реакцией, почему бы не реализовать это только для файлов *.tsx, пока он не достигнет этапа 3?

Редукторы Redux также могут быть записаны в виде файлов *.tsx, простообъект экземпляры приведения типов должны быть преобразованы в obj как тип

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

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

пинг @mhegazy

Обновил теги. Я должен уточнить, что причина, по которой это еще не реализовано, заключается не в ценности, а в сложности реализации системы типов. само преобразование довольно тривиально, т.е. {...x} в Object.assign({}, x) . проблема в том, как эти типы предварительно тестируются и как они себя ведут. например, для параметра универсального типа T есть {...T} назначаемое T , как насчет {x: number, ...T} , что если T имеет методы, и т.д.. https://github.com/Microsoft/TypeScript/issues/10727 содержит более подробное объяснение необходимых изменений системы типов.

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

внедрение синтаксической поддержки для распространения свойств + наивная типизация Object.assign было бы очень полезным временным решением, пока мы ждем «правильной» реализации. Не позволяйте совершенству быть врагом хорошего.

Похоже, что предложение достигло стадии 3: https://github.com/tc39/proposals

@ddaghan ваш пример с tsx не будет работать

какие сроки для этой функции?

@ SpyMaster356 Я некоторое время @sandersn пинал задницы по этому поводу (по крайней мере) последние несколько недель. 🙌

Вы можете следовать здесь (https://github.com/Microsoft/TypeScript/pull/12028) и здесь (https://github.com/Microsoft/TypeScript/pull/11150).

Кто-то должен обновить дорожную карту

Кажется, что использование этой функции позволяет назначать неизвестные реквизиты:

interface State {
  a: string;
  b: number;
}

let state: State = { a: "a", b: 0 };

state = {...state, x: "x"}; // No error

Я ожидал ошибки, что x не является известным свойством State . Разве не так работает эта функция?

Например, мой текущий обходной путь до этого PR был следующим:

state = update(state, { x: "x" }); // Error: Property 'x' is missing in type 'State'.

function update<S extends C, C>(state: S, changes: C): S {
  return Object.assign({}, state, changes);
}

Можно ли добиться этого с помощью распространения/отдыха объекта?

Объект Rest and Spread, согласно предложению ES , ведет себя аналогично Object.assign . последнее упоминание свойства «выигрывает». об ошибках не сообщается. в вашем примере тип {...state, x:"X"} равен { a: string, b:number, x:string } ; и этот тип можно присвоить State и, таким образом, присваивание работает. это то же самое, что сказать, что let state: State = { a: "a", b:0, x: "X" }; будет разрешено.

Но это то, что меня смущает: let state: State = { a: "a", b:0, x: "X" } дает ошибку Object literal may only specify known properties, and 'x' does not exist in type 'State' чего я и хочу... почему это действительное назначение при выходе из литерала объекта, содержащего распространение?

извините .. литералы объектов - это особый случай. мой пример был неверным. вот лучший пример:

let obj = { a: "a", b:0, x: "X" };
let state: State = obj; // OK

Вопрос здесь в том, если довольно субъективно. Когда компилятор видит let state:State = { a: "a", b:0, x: "X" } , скорее всего, x является опечаткой, либо устаревшим свойством, которое было опущено после рефакторинга, либо типом необязательного свойства, поэтому оно сообщается как ошибка.

однако, если вы распространяете объект, скажем, let myConfig : State= { a: 1, ...myOtherBigConfigBag} , если у myOtherBigConfigBag есть несколько свойств, которые вам не нужны, вам просто нужны a и b , ошибка здесь заставит вас синхронизировать эти два интерфейса или выполнить приведение, чтобы эти ошибки исчезли.

что сказал. мы должны пересмотреть это решение. подал https://github.com/Microsoft/TypeScript/issues/12717, чтобы отслеживать это.

Я понимаю. Мне нравится твоя идея в #12717, это именно то, о чем я думал. На самом деле мне бы хотелось такого поведения даже для реквизитов распространения, но я понимаю вашу точку зрения, что это очень субъективно и может раздражать в некоторых распространенных случаях использования JS.

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

Привет, ребята, поздравляю с отличным релизом! Настало время заслуженного отдыха... кстати говоря, у меня проблемы с оператором отдыха :)

Используя React, у вас обычно есть такой компонент, как:

export interface MyLinkProps extends React.HTMLAttributes {
    myUrl: string
}

class MyLink{

    render(){
      const {myUrl, ...attrs } = this.props;
     return <a href={calculate(myUrl)} ...attrs>Click here</a>;
   }
}

Проблема в том, что когда вы наводите указатель мыши на attrs вы получаете список всех свойств (сотни) вместо React.HtmlAttributes.

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

Некоторые альтернативы:

    const {myUrl, ...attrs as React.HtmlAttributes } = this.props; //Is not really a casting

    const {myUrl, ...attrs : React.HtmlAttributes } = this.props; //conflicts with ES6?

    const attrs : React.HTMLAttributes; 
    const { myUrl, ...attrs } = this.props;  //re-declare the variable

@bondz Ну, это не так в 100% случаев использования в моем проекте. :) Но в другом проекте это может сильно раздражать. В моем случае я использую Redux и React и интенсивно использую неизменяемое состояние, что означает, что для обновления или создания объектов состояния я должен скопировать все реквизиты в литерал нового объекта... во всех случаях я хочу 100% безопасность типов, которую я Я пытаюсь скопировать правильные данные на интерфейс целевого состояния. В настоящее время я использую функции для обеспечения этой безопасности, но я бы предпочел использовать распространение объектов, потому что это чище (читабельный, выразительный, стандартный синтаксис). Но в чужом проекте им может потребоваться другое поведение, потому что они используют много нетипизированных или свободных объектов. структур, так что я вижу, как это довольно субъективно. Я думаю, что предложение # 12717 является хорошей золотой серединой (и более совместимо с существующей безопасностью литерала объекта).

https://github.com/Microsoft/TypeScript/issues/2103#issuecomment-145688774
Мы хотим дождаться, пока предложение достигнет этапа 3, прежде чем решать эту проблему.

Кажется, это уже состояние 3, есть ли план поддержки этого?

Кстати, мы используем VSCode в основном для разработки ES, а не TS :)

@evisong эта функция уже доступна в последней версии vsCode. Обновите vsCode до версии 1.8.

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