Repository navigation
inconsistency in type inference on union of function types #9104
Description
Activity
Artazor commented
on Jun 12, 2016 ContributorAuthorMore actionsIt would be nice if
string & number = never // type error
All incomatible primitive types should produce
neverwhen intersected.
Ifneverdetected in any input place it means type error.This behavior would allow expressing the following function union type:
type F1 = (a: A1, b: B1, c: C1) => R1 type F2 = (a: A2, b: B2) => R2 type F3 = F1 | F2; // (a: A1 & A2, b: B1 & B2, c: C1) => R1 | R2
thus
string & (string | number) = (string & string) | (string & number) = string | never = string
which allow assigning the correct type for the original issue.
Code in type domain at least should compile.
See #4612
RyanCavanaugh commented
on Oct 24, 2016 MemberMore actionsDuplicate #10025
- addedDuplicateAn existing issue was already createdAn existing issue was already created
on Oct 24, 2016 All incomatible primitive types should produce never when intersected.
However, unless--strictNullChecksis enabled, all primitive types are somewhat compatible, as:
const X:string&number = undefined;is valid.
So when--strictNullChecksis enabledstring&number = never, elsestring&number=undefined.For the "real issue":
I propose UNION function types to work like that:(A1:T1_1,A2:T1_2,...)=>R1|(A2:T2_1,A2:T2_1,...)=>R2 = (A1:T1_1&T2_1,A2:T1_2&T2_2,...)=>R1|R2In addition I propose:
- in case an argument is optional in only one function, it is not optional in result
- in case an argument is optional in both functions, it is optional in the result
- in case the count of arguments differs, the types of the missing arguments is assumed as any:
(A1:T1_1)=>R1|(A1:T2_1,A2:T2_2)=>R2 = (A1:T1_1&T2_1,A2:any&T2_2)=>R1|R2I know, the last could be seen to differ from how TypeScript handles function types in general, but it is closer to what ECMAScript allows:
var f1 = (A1) => {}; var f2 = (A1,A2) => {}; var b:boolean; var f = b?f1:f2; f1(1,2); // is valid ECMAScript f(1,2); // is valid and commonly done ECMAScriptIn TypeScript:
var f1 = (A1:number) => {}; var f2 = (A1:number,A2:number) => {}; var b:boolean; var f = b?f1:f2; f1(1,2); // fails (I love TypeScript for letting that fail) f(1,2); // I propose this should not fail, even though the compiler identifies, that f could be f1 at runtimeI appreciate, that TypeScript requires the exact amount of parameters to be passed, but it shall be less picky in case of union function types. This allows us to simply run the method with two parameters - additional parameters are ignored if not required by the actual implementation.
For the Intersection function type it should be the same, except for the return type:
(A1:T1_1,A2:T1_2,...)=>R1&(A2:T2_1,A2:T2_1,...)=>R2 = (A1:T1_1&T2_1,A2:T1_2&T2_2,...)=>R1&R2The "VALUE DOMAIN"-Example from the initial post works in TS 2.0.3 as described, but in my opinion it IS wrong, that it compiles. See the following example:
// VALUE DOMAIN interface I1 { X:number; } interface I2 { Y:number; } const f1 = (arg: I1|I2) => { }; const f2 = (arg: I1) => { arg.X = 42; }; var x:boolean = doSomeSomethingAndReturnBoolean(); const fn1 = x?f1:f2; var res1 = fn1({Y:123}); // compiles, but at runtime fn1 = f2, which expects I1 as argument!!!I expect, that the resulting fn1 can only be invoked with an I1 as argument, but not an I2, as it may be of type (arg:I1)=>void at runtime, which the compiler can detect with the rules described above.
- locked and limited conversation to collaborators
on Jun 19, 2018
TypeScript Version:
1.8.10 (seems, that other are affected as well)
Code
Expected behavior:
(arg: string) => stringand should not be dependent on the order of union members / conditional branches.Actual behavior: