Skip to content

Spread type - #13288

Closed
Nathan Shively-Sanders (sandersn) wants to merge 11 commits into
masterfrom
spread-type
Closed

Spread type#13288
Nathan Shively-Sanders (sandersn) wants to merge 11 commits into
masterfrom
spread-type

Conversation

@sandersn

Copy link
Copy Markdown
Member

Fixes#10727
Fixes#11100

Follow-up to #11150 — most discussion has already taken place there; this PR just updates the previously-unreleased spread type code.

@sandersn

Copy link
Copy Markdown
MemberAuthor

Anders Hejlsberg (@ahejlsberg) this is the PR I was talking about.

}
const type = checkExpression((memberDecl as SpreadAssignment).expression);
if (!isValidSpreadType(type)) {
if (type.flags & (TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.BooleanLike | TypeFlags.EnumLike | TypeFlags.ESSymbol)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should just change the definition of isValidSpreadType

@dxiao

Copy link
Copy Markdown

Any chance this can merge in sometime for 2.3.3? Proving kinda annoying to deal with...

@ccschmitz

Copy link
Copy Markdown

Same for us, David Xiao (@dxiao). We get a pile of new errors on 2.3. Holding out on the upgrade because of it. We are holding out on the upgrade in hopes a fix like this makes it out in a patch release.

@mhegazy

Copy link
Copy Markdown
Contributor

This is a new feature, and will not be added in a patch release.

@alex-sherwin

Alex Sherwin (alex-sherwin) commented May 8, 2017

Copy link
Copy Markdown

This is a new feature, and will not be added in a patch release.

I think people who were already using the spread operator in TSX would argue, purely from a consumer of TypeScript perspective, this is not a new feature, but a regression.

If it's not going to make it into a patch release, then we're waiting until at least 2.4, which is how far off? So anyone who wants to use spread operators in TSX (which is pretty much a requirement for React + react-router) we're going to have to skip 2.3 entirely

@prencher

Copy link
Copy Markdown

Mohamed Hegazy (@mhegazy) This use case broke with 2.3 from 2.2, even if the behavior may not have been correct prior to 2.3. I would highly urge you to fix it in 2.3, it is a major regression.

@jvbianchi

Copy link
Copy Markdown

Is this ready for merge?

@sandersn

Copy link
Copy Markdown
MemberAuthor

Victor Bianchi (@jvbianchi) No. See preceding discussion.

@robyoder

Rob Yoder (robyoder) commented Jan 24, 2018

Copy link
Copy Markdown

Nathan Shively-Sanders (@sandersn) Your July 18 comment doesn't address positive extend scenarios, or maybe I'm misunderstanding. If I have T extends { a: string } I should be able to do const newThing: T = { ...oldThing, a: "something" }; for oldThing: T. Right?

Your comment seems to acknowledge the difficulties with assigning properties to completely unknown Ts, but if you have expressed what T extends, you should be able to spread T with those properties because the types are defined.

@sandersn

Copy link
Copy Markdown
MemberAuthor

Rob Yoder (@robyoder) what if oldThing: { a: "foo" | "bar" } ? Then the assignment is incorrect because "something" isn't foo or bar.

@robyoder

Copy link
Copy Markdown

Yeah that's unfortunate. Too bad there's no way to force invariance in certain places.

@robyoder

Rob Yoder (robyoder) commented Jan 24, 2018

Copy link
Copy Markdown

Nathan Shively-Sanders (@sandersn) but variance isn't really a holdup, right? TS already fails there:

constfunc=<Textends{a: string}>(param: T): T=>{constcloned: T=clone(param);cloned.a="something";returncloned;};interfaceFoo{a: "foo"|"bar";}constdata: Foo={a: "foo"};constdata2: Foo=func(data);// this type checks, but `a` is now "something", not "foo" or "bar"

@sandersn

Copy link
Copy Markdown
MemberAuthor

Rob Yoder (@robyoder) Good point. What is the scenario you would like to work? Constraining T to require the properties you are about to add doesn't help the React HOC scenario as far as I understand it, because the whole point is that the caller doesn't have those properties.

@robyoder

Copy link
Copy Markdown

Yeah, I'm not using React HOCs atm. In this specific case, I have an object with a default id of 0, and I'm saving it to storage and getting it back with the id set properly. So something like this:

constaddEntry=<Textends{id: number}>(entry: T): T=>{constid=entry.id>0 ? entry.id : getTheNextId();constnewEntry={ ...entry, id };returnsaveToStorage(newEntry);// returns newEntry after saving it}

And right now that obviously gives me the error Spread types may only be created from object types.

@DJWassink

Copy link
Copy Markdown

Just a little headsup for people ending up here. It seems like Object.assign does work with generic method, little example I found msyself doing in my code:

publicaddSomethingToBar<TextendsIBar>(bar: T): T&{foo: number[]}{returnObject.assign({},bar,{foo: [1,2]});}

@burabure

Copy link
Copy Markdown

any updates on this?, we really need something better than

// Cant use spread here because of TS issue, see TypeScript #10727 #13288// tslint:disable-next-line:prefer-object-spreadthis.setState(prevState=>Object.assign({},prevState,newState))...

@zheeeng

Copy link
Copy Markdown

expecting on progress.

@lukescott

Luke Scott (lukescott) commented Apr 12, 2018

Copy link
Copy Markdown

I am hitting this issue all the time since I work with immutable objects frequently with Redux.

@Ky6uk

Roman Nuritdinov (Ky6uk) commented Apr 13, 2018

Copy link
Copy Markdown

Hmm. I am also getting the issue when trying to use generics for React HOCs with spread params.

interfaceInjectedProps{readonlyp1: string;readonlyp2: number;}functionhoc<PextendsInjectedProps>(Component: React.ComponentType<P>){// ERROR: Rest types may only be created from object types.return({ p1, p2, ...rest} : P)=><Wrapperp1={p1}p2={p2}><Component{...rest}/></Wrapper>;}

Do we have an alternative version for solving the issue?

@lukescott

Copy link
Copy Markdown

Besides using Object.assign, you can tack on as any, but then you lose the strict typing. Not ideal.

@goodmind

Copy link
Copy Markdown

Use this

/** * Get all names of properties with types that include undefined. */exporttypeOptionalPropNames<T>={[KinkeyofT]: undefinedextendsT[K] ? K : never}[keyofT];/** * Common properties from L and R with undefined in R[K] replaced by type in L[K] */exporttypeSpreadProps<L,R,KextendskeyofL&keyofR>={[PinK]: L[P]|Exclude<R[P],undefined>};/** * Type of `{ ...L, ...R }` / `Object.assign(L, R)`. */exporttypeSpread<L,R>=/** properties in L that don't exist in R */Pick<L,Exclude<keyofL,keyofR>>/** properties in R with types that exclude undefined */&Pick<R,Exclude<keyofR,OptionalPropNames<R>>>/** properties in R, with types that include undefined, that don't exist in L */&Pick<R,Exclude<OptionalPropNames<R>,keyofL>>/** properties in R, with types that include undefined, that exist in L */&SpreadProps<L,R,OptionalPropNames<R>&keyofL>;

@joaovieira

Copy link
Copy Markdown

Bump. Where is this going? Still waiting.. 😕

@ColCh

Max Sysoev (ColCh) commented Jun 8, 2018

Copy link
Copy Markdown

<offtop>
#13288 (comment) yeah we have generic jsx, conditional types, other kill-features, but no rest props type, even in TS 3.0 in master branch by this very moment

https://github.com/Microsoft/TypeScript/blob/f17fe8713e05756babc72720e091bbefc83f5135/package.json#L5

original commit in this PR was created as TS was 2.2

https://github.com/Microsoft/TypeScript/blob/c2562b43ddb2e3df01b8892302579e3d26dace6f/package.json#L5

it seems that this is simply not a priority :(
</offtop>

hey Nathan Shively-Sanders (@sandersn)Anders Hejlsberg (@ahejlsberg), I'm sorry for mentioning you, but it seems that this PR got lost in stream of other issues? Any possibility to see this in TS@3 or later?

@mfferreira

Copy link
Copy Markdown

Bump

@perjerz

Copy link
Copy Markdown

any update?

@RyanCavanaugh

Copy link
Copy Markdown
Member

As everyone can probably tell, this PR didn't get merged and is now painfully out of date.

Mapped and conditional types meet a very large proportion of the use cases here. Here's an example of combining two types including exclusion and overwriting:

typeType1={x: number,y: number};typeType2={name: string,kind: string,y: string;};typeSpread<T1,T2,TExclude>={[KinExclude<keyofT1,TExclude|keyofT2>]: T1[K]}&{[KinExclude<keyofT2,TExclude>]: T2[K]};typeType3=Spread<Type1,Type2,"name">declareconstt3: Type3;t3.

image

We'd like people bumping/thumbing/pinging this PR to try using these sorts of types in their scenarios to see what other use cases still need to be designed around if we do include an explicit spread type syntax - see the original PR comments for links. Thanks!

@microsoftMicrosoft (microsoft) locked and limited conversation to collaborators Aug 10, 2018
@jakebailey
Jake Bailey (jakebailey) deleted the spread-type branch November 7, 2022 17:30
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

20 participants

@sandersn@dxiao@ccschmitz@mhegazy@alex-sherwin@prencher@syabro@Igorbek@rahumble@tvald@DylanRJohnston@zheeeng@kitsonk@leebenson@jshaker@jvbianchi@robyoder@DJWassink@burabure@lukescott