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
{{ message }}
Repository navigation
Improper Assignment Allowed on Properties of Unioned Interface type #14369
// A *self-contained* demonstration of the problem follows...interfaceIDogArgs{paws?: number;}classDog{constructor(args: IDogArgs){}}interfaceICatArgs{claws: string;}classCat{constructor(args: ICatArgs){}}interfaceIHowToMakeDog{ctor: new(args: IDogArgs)=>Dog;args: IDogArgs;}interfaceIHowToMakeCat{ctor: new(args: ICatArgs)=>Cat;args: ICatArgs;}typeAnimalMakers=IHowToMakeDog|IHowToMakeCat;// OKvartestDog: AnimalMakers={ctor: Dog,args: {paws: 4}};vartestCat: AnimalMakers={ctor: Cat,args: {claws: 24,// Why is this okay?bogus: false// What is this okay?}};
Expected behavior:
Should be restricted from assigning a number to the claws property.
Should be restricted from assigning "bogus" property. Actual behavior:
The above code is valid and passed typescript compiler checks, and I am able to make an assignment that does not meet the requirements of any of the types specified in the AnimalMakers union type. The key seems to be that the paws property on IDogArgs is optional. If this property is required, the compiler provides errors on the testCat assignment as expected. Also, the compiler correctly provided errors in this scenario as of TypeScript 1.8.
IDogArgs is an interface with all optional arguments, that makes is look like {} from an assignablity perspective.
A type is assignable to a union type iff it is assignable to at least one constituent. since { claws: number } is assignable to {} and {paws? : number}, the check is allowed.
Thanks. The example I provided seems a little different though, because of the ctor property. Since I have supplied the Cat constructor for the ctor arg in the testCat assignment, this cannot be an assignment to IHowToMakeDog type (which has the {} args type), and it should require ICatArgs for for the args property. In other words, the assignment is clearly discriminated by the ctor property. #7845 does not include such a discriminator, and my example was working as of TypeScript 1.8 (which 7845 cites as the version that they experienced their problem with).
Yes, that is not what I want, but your feedback brought me a lot of clarity on my issue. Something changed in the latest TypeScript because even though the ctor argument will produce a class with an identical structure, it seems TS1.8 used to discriminate on the actual type of class produced. It seems my issue can be distilled to the following code:
classCat{}classDog{}interfaceCatMaker{ctor: new()=>Cat}vartest: CatMaker={ctor: Dog// Why is this allowed?}
Cat and Dog are structurally compatible. so a thing that makes a Dog is assignable to a thing that makes a Cat. This specific behavior has not changed since 1.0 i would say.
You are spot on - I did some more testing against TS1.8, and the real reason my case in the original post fails is because the args property wasn't assignable to ANY of the args property types in the union type. Changing the ctor as a "discriminator" in this example has no impact in TS1.8. However, in TS1.8 { claws: 24, bogus: true } was not assignable to { paws?: number }, but now it is in TS2.1, and that's why this surfaced for me. I was apparently under a misunderstanding about how the type checking was working all along. #7485 thus exactly describes my issue. Thanks again for your help.
Activity
mhegazy commentedon Feb 28, 2017
IDogArgsis an interface with all optional arguments, that makes is look like{}from an assignablity perspective.A type is assignable to a union type iff it is assignable to at least one constituent. since
{ claws: number }is assignable to{}and{paws? : number}, the check is allowed.This is another manifestation of #7485
mathias999us commentedon Feb 28, 2017
Thanks. The example I provided seems a little different though, because of the ctor property. Since I have supplied the Cat constructor for the ctor arg in the testCat assignment, this cannot be an assignment to IHowToMakeDog type (which has the {} args type), and it should require ICatArgs for for the args property. In other words, the assignment is clearly discriminated by the ctor property. #7845 does not include such a discriminator, and my example was working as of TypeScript 1.8 (which 7845 cites as the version that they experienced their problem with).
mhegazy commentedon Feb 28, 2017
The two types
CatandDoghave no properties. that makes both look like{}from a structural type system perspective.Adding any properties on both types should also trigger the error on the assignment. but i am assuming this is not what you want.
mathias999us commentedon Feb 28, 2017
Yes, that is not what I want, but your feedback brought me a lot of clarity on my issue. Something changed in the latest TypeScript because even though the ctor argument will produce a class with an identical structure, it seems TS1.8 used to discriminate on the actual type of class produced. It seems my issue can be distilled to the following code:
This still seems like undesirable behavior to me.
mhegazy commentedon Mar 1, 2017
CatandDogare structurally compatible. so a thing that makes aDogis assignable to a thing that makes aCat. This specific behavior has not changed since 1.0 i would say.mathias999us commentedon Mar 1, 2017
You are spot on - I did some more testing against TS1.8, and the real reason my case in the original post fails is because the args property wasn't assignable to ANY of the args property types in the union type. Changing the ctor as a "discriminator" in this example has no impact in TS1.8. However, in TS1.8 { claws: 24, bogus: true } was not assignable to { paws?: number }, but now it is in TS2.1, and that's why this surfaced for me. I was apparently under a misunderstanding about how the type checking was working all along. #7485 thus exactly describes my issue. Thanks again for your help.