You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Right now I am working in the branch object-rest-spread-WIP if you want to follow along.
Reacted by Tingan Ho, Aluan Haddad, Oliver Joseph Ash, Akim, ikokostya, Marvin Hagemeister, Andrey, kgtkr, Guillaume RODRIGUEZ, Ramrami Mohamed and 2 more
Reacted by Scott Harrison, Akim, Andrey and Guillaume RODRIGUEZ
I think you explained why in IRC, but I'm not sure I completely understand. Are you saying in the above example we're trying to assign number to string & number which is an error, but if we try to assign string & number to number then it is allowed?
DerivedClass is assignable to BaseClass because all DerivedClass are also BaseClass. Similarly string & number is assignable to number because all string & number are also number.
BaseClass is not assignable to DerivedClass because an arbitrary BaseClass is not necessarily also a DerivedClass. Similarly number is not assignable to string & number because an arbitrary number is not necessarily also a string.
// number is not assignable to string & number because an arbitrary number is not necessarily also a string.constx='foo'asstring&numberconstx1: number=x// string & number is assignable to number because all string & number are also number.constx2: string&number='foo'
i.e. all type features of target are preserved, with only properties from U tacked on. This covers the very common use case of adding properties to functions.
Daniel Earwicker (@danielearwicker) that's not quite right either because if the function already has a property that source overwrites then intersection doesn't give the right answer either. If we take as given that our only operators are spread and intersection, then I'd go with the stricter and more accurate one. (Of course we could introduce two kinds of spread but I don't like that idea.)
Can you point me to some source that spreads properties into functions? I'm always interested in collecting uses of the spread type — previously I thought that real-world uses were always with property bags.
Yes, I see. I haven't been using object spread operator for this purpose (I only use TS) but I have been using Object.assign and similar functions to do the same thing.
Hiding in plain sight, jQuery's $ is probably the famous example of a function with properties. It's very handy to be able to encapsulate some capabilities in an object that is also directly callable, giving succinct access to one preferred way of using it.
I've been working on a strongly-typed composable Redux library where I make augmented functions this way. Although as you can see, I've simply defined my own assign function so I can control the type signature anyway, so I can keep using intersection and you can ignore me! :)
Metod Ribič (@metodribic) waiting on a spread type, which is waiting on a difference type to make sure JSX Higher-order-component scenarios work. Now that some kind of difference types are expressible using conditional types, the scenario may work. The conversation is happening at #10727.
This type definition of Object.assign can fixed in now using utility-types's assign definition https://github.com/piotrwitek/utility-types#assign (maybe steal implementations), but we should wait for type spreading features?
{
x: number
y?: number | undefined
m: () => number
m2: () => string
}
Meaning that it isn't correctly combining optional properties from the right, and isn't correctly discarding class-originating methods, which are on the prototype and will be discarded by Object.assign at runtime.
The problem with that example is that we don't model Object.assign's mutation of its first argument. That's not possible in Typescript today, since we'll just use the declared type of a all the way through the program. The control flow graph could allow us to model mutations to a type, but right now we only narrow types by removing constituents of a union type. And of course a doesn't have a union type.
Activity
Arnavion commentedon Sep 23, 2016
As I explained on IRC, the result of
assignis{ foo: string & number }, so TS considers it okay to be assigned to{ foo: string }.sandersn commentedon Sep 23, 2016
I am close to having a PR for spread types, so when those are in,
Object.assignwill change to have the correct type:assign<T, U>(target: T, source: U): { ...T, ...U }Right now I am working in the branch
object-rest-spread-WIPif you want to follow along.sandersn commentedon Sep 23, 2016
(also you'll just be able to use the syntax
{ ...state, foo: 1 }instead of calling Object.assign)Arnavion commentedon Sep 23, 2016
Nathan Shively-Sanders (@sandersn) So with your change, the result of
assignwould be{ foo: number }?sandersn commentedon Sep 23, 2016
Yes, which is not assignable to
State, so it would error as desired.OliverJAsh commentedon Sep 24, 2016
Thanks, that's great Nathan Shively-Sanders (@sandersn). I look forward to it!
Arnav Singh (@Arnavion) How come this works?
I think you explained why in IRC, but I'm not sure I completely understand. Are you saying in the above example we're trying to assign
numbertostring & numberwhich is an error, but if we try to assignstring & numbertonumberthen it is allowed?Arnavion commentedon Sep 24, 2016
DerivedClassis assignable toBaseClassbecause allDerivedClassare alsoBaseClass. Similarlystring & numberis assignable tonumberbecause allstring & numberare alsonumber.BaseClassis not assignable toDerivedClassbecause an arbitraryBaseClassis not necessarily also aDerivedClass. Similarlynumberis not assignable tostring & numberbecause an arbitrarynumberis not necessarily also astring.OliverJAsh commentedon Sep 24, 2016
That makes sense, thank you Arnav Singh (@Arnavion):
danielearwicker commentedon Oct 12, 2016
Nathan Shively-Sanders (@sandersn), this looks very good! Shouldn't the new type of
Object.assignbe:i.e. all type features of target are preserved, with only properties from U tacked on. This covers the very common use case of adding properties to functions.
sandersn commentedon Oct 12, 2016
Daniel Earwicker (@danielearwicker) that's not quite right either because if the function already has a property that
sourceoverwrites then intersection doesn't give the right answer either. If we take as given that our only operators are spread and intersection, then I'd go with the stricter and more accurate one. (Of course we could introduce two kinds of spread but I don't like that idea.)Can you point me to some source that spreads properties into functions? I'm always interested in collecting uses of the spread type — previously I thought that real-world uses were always with property bags.
danielearwicker commentedon Oct 12, 2016
Yes, I see. I haven't been using object spread operator for this purpose (I only use TS) but I have been using
Object.assignand similar functions to do the same thing.Hiding in plain sight, jQuery's
$is probably the famous example of a function with properties. It's very handy to be able to encapsulate some capabilities in an object that is also directly callable, giving succinct access to one preferred way of using it.I've been working on a strongly-typed composable Redux library where I make augmented functions this way. Although as you can see, I've simply defined my own
assignfunction so I can control the type signature anyway, so I can keep using intersection and you can ignore me! :)10 remaining items
falsandtru commentedon Apr 3, 2017
Now I'm using the following signatures.
dgreene1 commentedon Jul 18, 2017
Subscribing for updates.
metodribic commentedon Feb 14, 2018
What is the current status on this?
sandersn commentedon Feb 14, 2018
Metod Ribič (@metodribic) waiting on a spread type, which is waiting on a difference type to make sure JSX Higher-order-component scenarios work. Now that some kind of difference types are expressible using conditional types, the scenario may work. The conversation is happening at #10727.
airtoxin commentedon Mar 17, 2018
This type definition of Object.assign can fixed in now using utility-types's assign definition https://github.com/piotrwitek/utility-types#assign (maybe steal implementations), but we should wait for type spreading features?
sandersn commentedon Mar 19, 2018
Ryoji Miyazato (@airtoxin) would utility-type's Assign get this example right?
sandersn commentedon Mar 19, 2018
Currently it produces
Meaning that it isn't correctly combining optional properties from the right, and isn't correctly discarding class-originating methods, which are on the prototype and will be discarded by Object.assign at runtime.
zuzusik commentedon Mar 30, 2018
Nathan Shively-Sanders (@sandersn) would your work also fix this example?
sandersn commentedon Mar 30, 2018
(Reprinting example for ease of reading)
The problem with that example is that we don't model
Object.assign's mutation of its first argument. That's not possible in Typescript today, since we'll just use the declared type ofaall the way through the program. The control flow graph could allow us to model mutations to a type, but right now we only narrow types by removing constituents of a union type. And of courseadoesn't have a union type.iflp commentedon Jul 23, 2018
A workaround could be:
The trade-off being an extra line of code for type safety.
RyanCavanaugh commentedon Aug 15, 2018
Tracking at #10727