Typescript: 支持提议的 ES Rest/Spread 属性

创建于 2015-02-21  ·  96评论  ·  资料来源: microsoft/TypeScript

es7 提案: https :

传播属性

打字

在我看来,这种方法的目标是能够复制一个对象并更改一些道具,所以我认为在这种情况下不检查重复的属性声明尤为重要:

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

我的小jsx-typescript fork 中有一个非常幼稚的类型检查算法,用于类似的功能( JSXSpreadAttribute ):当我遇到传播对象时,我只是在属性表中复制 _spread object_ 的属性,并在我遇到具有相似名称的声明时覆盖这些属性。

发射

jstransform使用Object.assignbabel引入一个 shim:

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

我们可以强制在ObjectConstructor接口上存在assign函数,或者提供类似的函数(使用不同的名称)。

我认为最佳解决方案是不在es6目标中发出任何帮助程序(或者如果Object.assign已定义),并为es5es3发出帮助程序函数

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

_编辑:添加了一些小打字/发射示例_

Committed ES Next Fixed Suggestion

最有用的评论

我不确定我们最终将如何发布它,但我开始研究对象文字中的 rest/spread 和 destructuring 。 现在的计划是使用我们的__assign polyfill 进行发射。

所有96条评论

我们可能需要一些示例来说明如何在下层合理地发出它(如果在范围内)。

@RyanCavanaugh ,我添加了一些小的发射示例和输入想法。
我想研究那个,因为无论如何它或多或少是一个 jsx 功能,我需要将它添加到我的 fork 中,但如果它被添加到 typescript 核心,那就更好了^^。

我最近通过这篇文章在 ES7 中发现了这个特性。 不得不说,真是太方便了!

很想在 TypeScript 中看到这个!

有任何更新吗? 希望在 1.6 中看到这一点,因为它在与 React 一起使用时会非常方便:smile:

@prabirshrestha作为记录,我们目前在master上有 JSX 的属性扩展运算符。

@DanielRosenwasser你能详细说明一下吗? 这是否意味着这适用于带有 --jsx 标志的 master ? 这包括休息属性还是仅传播属性

抱歉@mnpenner@prabirshrestha ,是的,我的意思是master@RyanCavanaugh比我更了解这一点,但我相信它只是传播属性。

JSX 支持传播_属性_,例如<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+ 特性,它阻止我们切换到 typescript; 如果可能的话,肯定会喜欢早点。

我们会在这里考虑 PR。 不过需要注意的是,为该功能连接类型系统并不是一项简单的任务。 如果您有兴趣仔细阅读它,我会保持增量,并从解析开始,然后发出然后类型检查。 如果您有任何问题, @sandersn应该能够为您提供帮助。

只是给那些有兴趣在 Redux 中使用它的人的说明。 我同意这个符号很方便。 但实际上,通过利用updeep库,我已经过得很好。 我为它创建了一些基本的类型,它工作得很好。 我什至有一个简单的库,它实现了基于updeep的存储和操作。 如果有人对此感兴趣,请与我联系。

在任何情况下, :+1: 用于 TS 中的扩展运算符。 :微笑:

@xogeny我很想看看那个库。 谢谢

@cur3n4我有一个存储库,我一直在玩这些想法。 它发生了很大的变化(因为这一切仍在发展中)。 我使用了一个特殊的模块,它最初利用了updeep并且该功能仍在库中。 但是我已经停止使用它,因为我发现很难维护必要的类型约束。 我怀疑 Typescript 是否有类似于Java 的super泛型类型约束的东西updeep (以及其他几个库,如lodashramda )可能是使类型更加安全。

但不要误会我的意思,我仍然认为传播运算符会非常有用。 它实际上可以让你用我不知道如何处理当前类型系统的语言安全而简洁地表达和做事情(_即_允许你描述对外部库的正确类型约束)。 目前,我为了安全而牺牲了简洁性(而updeep方法为了简洁而牺牲了安全性)。

在过去几个月使用 Babel 之后,将我的项目转换为 TS 并且不得不去重做所有这些。 很想看到这个实现!

:+1:

很想看到这个。

+1

必须有功能,请实施

+1

+1 比 Object.assign 更好的语法 + JSX 支持

+1 它是用于传递道具的反应开发必须具备的( var { checked, ...other } = this.props; 。请参阅反应文档

它是反应开发的必备品

老实说,这是一种方便,不是必须的。 该提案处于 TC39 的第 2 阶段。 TypeScript 团队已经明确表示,他们不想考虑在第 3 阶段之前实现并非源自 TypeScript 的东西。“+1”的地方是 TC39。

@kitsonk - 当诸如 react / etc 之类的框架在其文档中明确指出这些类型的东西时,您不能期望得到良好的采用。 人们将开始使用 Typescript,看到所有这些问题并回到 Babel。 这就是我所做的。

好吧,我认为我们正在谈论一个更广泛的话题。 没有一个浏览器供应商会实现不在第 3 阶段的任何东西(除非他们个人喜欢它)。 TC39 一直试图为他们的网络部分提供一定程度的组织。 TypeScript 已经被咬得很厉害,因为之前的枪法(模块,看看从那以后造成的混乱)。 我尊重他们正在尝试至少围绕他们愿意实施的内容和时间提供一些指导和结构。 对于应该使用什么标准,还有其他建议吗? 我不认为“因为 X 框架在他们的文档中提到它”会是一个好的选择。

像 react / etc 这样的框架在他们的文档中特别提到了这些类型的东西

他们将其标记为仅建议,然后继续指导您如何跳过箍来配置 Babel 6 以使用它。 我宁愿责怪框架依赖于仅处于规范草案中的句法技术。 就个人而言,我们在 Dojo 中被Object.observe烧毁了,我再次保持沉默,在技术进入 Dojo 的第 3 阶段之前尝试依赖它们。

特定的路径不仅仅是支持在发射时为 TypeScript 转换语法。 有所有需要遵循的类型推断。 我已经查看了规范草案以了解,但是符号去哪里了? 不可枚举的属性去哪儿了? 我假设在这两种情况下它们都会消失在以太中,但是将需要在后台进行各种类型拆分和合并,所以我完全可以理解 TypeScript 团队说“好吧,让我们推迟在此之前,直到 TC39 真的在这方面踢了轮胎”。

总的来说,我认为今天的 TS 非常有特色。 有一段时间(我认为大约是 1.4 ~ 1.5),我对缺少一些 ES2015 功能感到沮丧,但截至今天,TS 已经很好地赶上了标准。

当然,有时也有其他语言的功能羡慕。 例如,Babel 提供了几乎所有的 JS 提案,包括“实验性”的东西。 就个人而言,我很期待这个问题以及函数绑定运算符。

但同时我们必须承认,TS 团队比 Babel 更重视兼容性。 他们不想在发布之间更改代码的语义,或者只是删除一个特性,这就是 Babel 所做的。 对于一些(企业?)很重要的项目。

我也可以理解,MS 不想将大量资源投入到可能会被丢弃的功能上,我想到了Object.observe惨败。 实际上,他们说他们会考虑对这个进行 PR,例如: wink:。

过去,一些特性是在实验性编译器切换(例如异步)之后早期添加的,这意味着您必须选择加入并承认该特性可能会在未来的版本中更改或被删除。 对于最受欢迎的请求,也许可以再次这样做?

这里只是一个简短的说明。我们在采用 0-1 阶段提出的 ES 特性方面进展缓慢,主要是出于向后兼容性的考虑。我们确实密切关注 TC39 会议的讨论,并直接参与会议或会前的提案准备;当我们加入一个特性时,我们想知道这对依赖它们的用户意味着什么。
如果您查看TC39 流程文档,则第 1 阶段的功能可以预期“主要”接受后更改。类、模块、迭代器和几乎所有重要的 ES6 特性都是如此。
戴上我的用户帽子,对语法的破坏性更改可能会机械地响应,而对语义的破坏性更改可能很难捕捉和修复;对于使用任何这些功能的团队来说,这可能是一笔巨大的成本,和/或阻碍采用更新版本的工具。我们希望尽可能减少这种情况。这就是为什么我们采用了一项策略,在功能达到阶段 3 时默认启用功能,并且在此之前处于实验标志(例如装饰器)。
话虽如此,对象属性 spread 和 rest 在我们的路线图上,我们确实计划在未来的版本中解决它们。

也许值得考虑增加编译器注入一些 3rd 方扩展的能力,就像在 babel 中所做的那样?

另一个想法:如何支持预处理 typescript 源(使用 Babel)?

如果我们可以让 Babel(或任何库)将对象扩展扩展为Object.assign调用,那可能会足够好用吗? 它也有望推广到 Babel 中实现的其他实验性功能。

@kitsonk - 绝对是更广泛的话题,希望 github 有更好的讨论;)。 我不同意你的观点:

. TypeScript 已经被咬得很厉害,因为之前的枪法(模块,看看从那以后造成的混乱)。

他们开始自己的实现镜像 .NET 而不使用社区的任何东西,这是他们自己的错。

围绕他们愿意实施的内容以及何时实施一些指导和结构

它们实现了装饰器(stage 1)、类属性(stage 1)等。

我不认为“因为 X 框架在他们的文档中提到它”会是一个好的选择。

你在开玩笑吧? react 是最流行的框架,并且预计会在一段时间内保持这种状态。 不支持这将/已经真正损害了采用。

我认为 TypeScript 在追赶 Babel 方面有很多工作要做,它也面临很多挑战,例如:很难在现有应用程序中开始使用它,从 babel 迁移到 A2 的 typescript 是一场噩梦!,缺乏解耦插件使使用 Node 工作变得极其困难。

你在开玩笑吧? react 是最流行的框架,并且预计会在一段时间内保持这种状态。 不支持这将/已经真正损害了采用。

我非常尊重 React,但我不是在开玩笑。 它不是镇上唯一的游戏,而且肯定处于大炒作曲线上。 6 个月前是 React + Flux,现在 React + Redux 是当时的主流。 一年前,Angular 和 Polymer。 两年前是 Backbone 和 Ember。 六年前是 Dojo、MooTools、jQuery、gwt、ExtJS 等……

不要在这里开始框架战争 :exclamation:
{..., }语法非常漂亮,不管任何框架

他们开始自己的实现镜像 .NET 而不使用社区的任何东西,这是他们自己的错。

@amcdnl你到底指的是什么? 我只能假设您将模块称为命名空间问题,这与 .NET 无关,也不是 TypeScript 中模块的主要痛点之一。

+1 将此包含在 typescript 节点 js 中实验性地支持它

让 typescript = { ...typescript, object_spread }; // 是的,请。

可惜没有任何动静,我真的希望这能尽快达到第 3 阶段。 如果可能,将在 VSCode 中杀死语法错误突出显示

我想知道莎莎对游戏有什么改变吗?

我了解 TS 不希望提前实现不稳定的功能:资源有限、保护我们用户免受可能的重大更改等。

但是现在 Salsa 是 VS Code 默认的 JS 引擎,更新 JS 语法(至少只是语法,而不是完整的 TS 类型)的压力将会增加。 尤其是考虑到 Babel 的受欢迎程度。

Salsa 是否能够比 TS 更快地接受更广泛的语法?

+1

这将是 2.1 而不是 2.0? :哭:
https://github.com/Microsoft/TypeScript/wiki/Roadmap#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 删除 TypeScript 的 JSX 编译器所依赖的未记录的内部函数之前,这已经奏效了。 哎呀😃

对象传播是一个不同的提议,具有公认的相似语法(它甚至可能是由 Facebook 工程师提出的,并受到 JSX 等效项的启发,尽管我不确定)。

所以既然{...props}还不能工作,你将如何使用 TypeScript 实现类似的东西?

所以既然 {...props} 还不能工作,你将如何使用 TypeScript 来实现类似的东西

使用xtendhttps://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"
});

它有点冗长,但它_is_ 是 ES2015 原生的,所以如果你已经为此准备了一个 polyfill,那么你就足够了。

这个操作符是我们决定在项目中使用 babel 而不是 typescript 的主要原因之一。

彻底改变了处理对象不变的游戏。

作为替代方案,是否有任何计划可以让您像在 babel 中那样将自定义转换插入 TSC? 甚至可能基于相同的 API,这会很棒并且可以解决这样的问题。

作为替代方案,是否有任何计划可以让您像在 babel 中那样将自定义转换插入到 TSC

@Niondir是的。 只需搜索[Transforms]标签。 需要这些才能将正确的 async/await/generator 转换为 TypeScript 编译器:rose:

经过一番搜索,您是在引用这个吗? https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API

听起来不错。我只是找不到与该 API 的对象传播相关的任何内容。 也许基于 Compiler API 解决这个问题会是一个不错的小项目 :)

@Niondir很抱歉没有在我的原始消息中提供更好的帮助。 我说的是https://github.com/Microsoft/TypeScript/issues/5595 < 这将允许emit用于 ESNext 语法。 TypeScript 比 Babel 更复杂一些,因为它需要对你的代码有 _semantic_类型系统的理解,所以对象传播工作可能需要来自 TypeScript 团队。

拥有一个基于插件的发射器会使这更容易。 是的,您将使用编译器 API(或者可能在开始时使用fork编译器,直到插件系统公开+用于语义/扫描器逻辑)

这可能是另一个问题的问题,但不能添加一个选项“允许实验”并让 TypeScript 允许未来的事情吗? 例如,允许展开运算符并按原样输出。 之后我可以用 babel 处理它。

这是我正在从事的某些项目的必备功能,如果没有像这样的基本内容,我将无法迁移。 如果可以的话,那就太棒了。

我不确定我们最终将如何发布它,但我开始研究对象文字中的 rest/spread 和 destructuring 。 现在的计划是使用我们的__assign polyfill 进行发射。

这是我过去三个月听到的最好的消息!

不幸的是,事实证明,让它与泛型一起工作是一项很大的工作。 您必须正确支持这样的代码:

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将是Tid类型和number的交集。

所以@sandersn说的是你需要一个单独的类型运算符。

虽然我们肯定会从这里更好的打字解决方案中受益,但Object.assign已经存在并且天真地使用交集作为输出类型。 作为权宜之计,为什么我们不能让其余运算符完全按照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 中使用这些,所以在我已知的 props 上进行强类型化非常方便。 还有一段额外的代码使用了这些函数,对于反应特别方便:

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

在这种情况下, props的输入与this.props ,但不包含任何用于向下传输的道具(现在位于restProps

IMO,通过针对此问题发布非类型安全的解决方法并没有获得太多收益。

同意,我现在正在开发一个更安全的版本。

对此的另一个尝试:

主要对象扩展:

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

以及使用 react 扩展的示例:

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

应保留所有打字。 唯一困扰我的部分是道具创建者,因为它需要重复。 我想我可以破解它并假设解构对象位于_a但这似乎_dangerous_...
_NOTE _: _a甚至不是一个可行的hack,因为它相当于x

这是一个可以玩的小提琴

我在想返回一个数组可能更有意义,因为您可以根据需要重命名输出。

从头开始,这将阻止正确输入道具。

由于这里的大多数人都希望将它与 react 一起使用,为什么不只为 *.tsx 文件实现它,直到它达到阶段 3?

Redux reducer 也可以写成 *.tsx 文件,只是对象类型转换实例应转换为obj 作为 Type

这不仅对 react 非常有用,例如在 redux reducer 中也非常有用。

我认为这方面的标签和缺失的里程碑有点过时了。 因为这是为 TypeScript 2.1提交并正在处理的 (#10727)。 所以没有必要再争论它的价值了。

平@mhegazy

更新了标签。 我应该澄清,尚未实现的原因不是价值,而是类型系统实现的复杂性。 它本身的转换相当简单,即{...x}Object.assign({}, x) 。 问题是如何预先测试这些类型以及它们的行为方式。 例如,对于泛型类型参数T{...T}可分配给T ,那么{x: number, ...T}呢,如果T有方法呢,等等。 https://github.com/Microsoft/TypeScript/issues/10727对所需的类型系统更改提供了更深入的解释。

我完全明白,理想地处理此功能所需的类型系统增强并非微不足道,很高兴看到它们正在被热情地解决! 我只想重申,

在我们等待“正确”实现时,为属性传播 + 天真的类型 Object.assign 提供语法支持将是一个非常有用的权宜之计。 不要让完美成为美好的敌人。

该提案似乎已进入第 3 阶段: https :

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

是否可以通过对象传播/休息来实现这一目标?

根据ES 提议,Object Rest and Spread 的行为类似于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有一些你不关心的属性,你只需要ab ,这里的错误将迫使您保持这两个接口同步,或者强制转换以使这些错误消失。

那说。 我们应该重新考虑这一决定。 提交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 等级