Repository navigation
add object initializers #8545
Description
Activity
type assertion?
function extend(doThis: DoThis): DoBoth { return doThis as DoBoth; }
zpdDG4gta8XKpMCd commented
on May 10, 2016 AuthorMore actionsno, quite opposite
first off it will break if you do as you say
secondly, type assertions is a way for taking responsibility off the compiler, whereas I am looking for the opposite
the only thing i can think of other than type assertion would be to spread in the other object.
function extend(doThis: DoThis): DoBoth { return { z: undefined, ...doThis }; }
zpdDG4gta8XKpMCd commented
on May 10, 2016 AuthorMore actionsnice but what about primitives and functions in the
doThisposition?Spread should be sugar for object.assign, so no non-enumrable and only own properties. Not sure if this is what you had in mind.
zpdDG4gta8XKpMCd commented
on May 10, 2016 AuthorMore actionsnow as i read closer it looks like a solution
Object spread and rest is tracked by #2103
zpdDG4gta8XKpMCd commented
on May 12, 2016 AuthorMore actionsgonna need to reopen it
one more useful scenario
function initializeAsDoThis<T unlike null | undefined>(obj: T => DoThis) { // <-- possible syntax for object that needs to be initialized? obj.x = undefined; obj.y = undefined; } class C implements DoThis { constructor() { initializeAsDoThis(this); } }
zpdDG4gta8XKpMCd commented
on May 12, 2016 AuthorMore actionsSpread should be sugar for object.assign, so no non-enumrable and only own properties. Not sure if this is what you had in mind.
spread operator is only useful at creating new objects, but for the cases where we deal with an existing object we can't use it
i am not sure i understand what this sample is meant to do any why it can not be expressed using existing constructs.
zpdDG4gta8XKpMCd commented
on May 12, 2016 AuthorMore actionsproblem:
say we have 20 interfaces each with 10 properties
it's easy to manifest that a our class is going to implement all 20 of them- it's much harder to come up with a list of 20x10=200 declared/initialized properties of this class that currently need to be spelled inside the class (there is no way to take property declaration/initilization outside the class)
- we cannot utilize inheritance for this because it's very unlikely that our class hierarchy has 20 ancestors classes that each implement one of those 20 interfaces
- we cannot delegate property initialization to a sub routine (just like we can do in vanila JavaScript)
workaround: none, we have to declare and initialize 200 properties by hand
solution: with initilizers we could have 20 functions which we would call from the constructor, each initilizer would add 10 initialized properties of a corresponding interface, we need a way for TypeScript to acknowledge that these properties are there and the class should be considered fully initialized
zpdDG4gta8XKpMCd commented
on May 12, 2016 AuthorMore actionsdefinition:
object initializer - is a function/method with a parameter that has 2 types: in and out, at the call site the function expects a value of the in-type to be used as an arugument, after the function is called argument should be considered being of the out-type
function initialize<a>( value: a /* <-- in type */ => a & { x: number } /* <-- out type */ ): void { // <-- hypothetical syntax value.x = 100; } let value = {}; value.x; // <-- should not typecheck initialize(value); value.x; // <-- should typecheck
Reacted by ExE Bossso is this a different proposal for #8353?
zpdDG4gta8XKpMCd commented
on May 12, 2016 AuthorMore actionsI think it is different. Main difference is that rather than trying to
track all possible execution branches that might reassign a variable (which
makes it a very hard task) I am proposing a simpler task of enforcing a
type change within the immediate scope of a dedicated function without
accounting for anything might happen in a subroutine.
On May 12, 2016 7:47 PM, "Mohamed Hegazy" notifications@github.com wrote:so is this a different proposal for #8353
#8353?—
You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
#8545 (comment)we have talked about something similar proposals before; the main reason for aversion is complexity. once you mix in generics, these declarations become harder to read and understand.
23 remaining items
I think your suggestion and mine are not comparable in size. While you use generics and that adds
< T extends >,T => T &, and two types (the type before and the type after), mine addsthenand two identifiers (with their types). So basically any of them can be bigger or smaller depending on the size of the types and identifiers.On the other hand, I agree that having a second, virtual identifier, can be confusing and it might be even against TypeScript goals. I'll try to think about another suggestion without a virtual identifier, but I wouldn't go either to use generics + a syntax that is pretty similar to arrow functions, plus having a flow analyser which could be hard to implement and that I that is still undefined how it should work.
About choosing a name, you can choose an arbitrary style for the name of the second virtual identifier, such as
x then xAfteror such.zpdDG4gta8XKpMCd commented
on Aug 30, 2018 AuthorMore actionsgenerics are necessary because it's the way to generalize and express uncertainties and about your types, i can't see how you can go without them, you would have to reinvent them at some point or greately limit the scope of applicability of this feature
flow analysis is already implemented, we just need to make use of it:
declare var a: number | string; a = 'hey'; const text: string = a;
arrow syntax is already familiar to anyone who used callbacks
zpdDG4gta8XKpMCd commented
on Aug 30, 2018 AuthorMore actionssince the syntax concerns the types (not the JS expressions) it can be literally anything:
=>,->,::,>>,<!>,becomes, etcwhat's important is that syntax needs to be bound to the parameter in place
While I agree with everything you said, I don't see the uncertainty of types to need the use of generics. For instance, using the syntax I proposed (I use this one because I don't have yet a better option), the types here are exact and not uncertain:
interface Before { address: string; } interface After { addr: string; } function map(userb: Before then usera: After) { usera.addr = userb.address; delete userb.address; }
You know exactly what must be the type before and you know exactly what will it be after. And this could be mixed with generics if any of these types (the type before and the type after) are unkown:
function inlineMap<T,U>(arrayB: T[] then arrayA: U[], mappingFunc: (T) => U) { for (let i = 0; i < arrayB.length; i++) { arrayA[i] = mappingFunc(arrayB[i]); } }
zpdDG4gta8XKpMCd commented
on Aug 30, 2018 AuthorMore actionsproblem is that in my use cases i don't know much if anything at all about what objects will be passed into my initializer function
think of the mixin pattern, i want to turn my very own object into something that has
xandyproperties and able to be manipulated by changing themBeforeandAfterin your example are cute but what if i need the sameaddr/addressbehavior applied to something else?my point is that
Beforeshould be rather a generic, because you have no idea upfront what it will really bezpdDG4gta8XKpMCd commented
on Aug 30, 2018 AuthorMore actionsyour last example is not going to work without generics becauseTandUas declared (bare generics) have nothing to do with having{ addr: }or any other propsso you are proposing to pass a callback for mutating bare
TandUeach time? in the bottom of my heart i like it a lot (it resonates with the category theory where you morph abstract objects not knowing anything about their nature), but this is not how generics are used in TS typicallyand by this i mean that in TypeScript rather than passing 10 callbacks along with your generics, you instead simply require not a bare generic but something that has
x,y, andzof type, say,numberso that you can work off that, which leads us to<T extends { x: number, y: number, x: number }>zpdDG4gta8XKpMCd commented
on Aug 30, 2018 AuthorMore actionsbesides say you have
interface Circle { x: number; y: number; r: number; } interface Line { x1: number; x2: number, y1: number, y2: number; } interface Box { ... } interface Triangle { ... } interface Star { ... } interface NGon { ... }now i want to add
colorto all/any of them, according to you i need a special function for each single type of objectnow i want to add
labelto all/any of them, again i need a special function for each single type of objectit's just silly
You don't need 10 callbacks. I think I'm using the generics as they are being used in the definitions of TypeScript for arrays:
interface Array<T> { map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; }
And with the proposed syntax example, if you want to add colour to all of them you could just do:
function addColour<T extends {}>(primitive: T then primitiveWithColour: T & {colour: string}, colour: string) { primitiveWithColour.colour = colour; }
Generics are totally compatible with that, but they are just not mandatory.
zpdDG4gta8XKpMCd commented
on Sep 1, 2018 AuthorMore actionsthis feature has to be build around generics, generics are the main use case, the main use case calls for prime time support from the language, using non-generics would be a special case
10 callbacks are necessary if you don't want to deal with
extends, i can give you an example but too lazy to write it, ifextendsis not a problem then 10 callbacks are not requiredWhy it has to be built around generics? They can be used, but I don't see the need for mandatory usage of generics if you know exactly the type before and the type after the change.
zpdDG4gta8XKpMCd commented
on Sep 3, 2018 AuthorMore actionsi didn't say generics are mandatory, all i said that generics are the main use case while specific types are a special case, and the reason i brought it up is that the syntax should rather be favoring the main case, not the special case
- addedDeclinedThe issue was declined as something which matches the TypeScript visionThe issue was declined as something which matches the TypeScript visionand removedNeeds More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified
on Dec 16, 2021 RyanCavanaugh commented
on Dec 16, 2021 MemberMore actionsThis doesn't come up often enough to justify the investment necessary to create this behavior
Currently there are 2 ways to enforce implementing an interface over an object:
In both cases we deal with a newly created object, however there are also situations when an already created object needs to be extended to comply to some more elaborate interface.
A good example are mixins and scenarios alike:
I am not aware how to make the extend function type safe, other than copying each field from
doThisto the resulting object. Which might or might not be a solution (functions are harder to extend this way). If only TypeScript had a way to enforce an interface on the given object it would be very helpful.