diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80b61edd3657a..130eeb06e3efa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24323,18 +24323,30 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * and no required properties, call/construct signatures or index signatures */ function isWeakType(type: Type): boolean { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type as ObjectType); - return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && resolved.indexInfos.length === 0 && - resolved.properties.length > 0 && every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)); - } - if (type.flags & TypeFlags.Substitution) { - return isWeakType((type as SubstitutionType).baseType); - } - if (type.flags & TypeFlags.Intersection) { - return every((type as IntersectionType).types, isWeakType); + return isWeakTypeWorker(type) > 0; + + function isWeakTypeWorker(type: Type): -1 | 0 | 1 { + if (type.flags & TypeFlags.Object) { + const resolved = resolveStructuredTypeMembers(type as ObjectType); + return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && resolved.indexInfos.length === 0 && + resolved.properties.length > 0 ? (every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)) ? 1 : 0) : -1; + } + if (type.flags & TypeFlags.Substitution) { + return isWeakTypeWorker((type as SubstitutionType).baseType); + } + if (type.flags & TypeFlags.Intersection) { + let result: -1 | 0 | 1 = -1; + for (const t of (type as IntersectionType).types) { + const v = isWeakTypeWorker(t); + if (!v) { + return 0; + } + result = result > v ? result : v; + } + return result; + } + return 0; } - return false; } function hasCommonProperties(source: Type, target: Type, isComparingJsxAttributes: boolean) { diff --git a/tests/baselines/reference/weakTypeIntersections1.errors.txt b/tests/baselines/reference/weakTypeIntersections1.errors.txt new file mode 100644 index 0000000000000..3ef703e4f0319 --- /dev/null +++ b/tests/baselines/reference/weakTypeIntersections1.errors.txt @@ -0,0 +1,50 @@ +weakTypeIntersections1.ts(11,7): error TS2559: Type '() => null' has no properties in common with type 'Partial & ThisType<{ x: string; }>'. +weakTypeIntersections1.ts(12,7): error TS2559: Type '() => null' has no properties in common with type 'Partial'. +weakTypeIntersections1.ts(13,7): error TS2559: Type '() => null' has no properties in common with type 'Partial & EmptyInterface'. +weakTypeIntersections1.ts(15,7): error TS2559: Type '() => null' has no properties in common with type 'Partial & Partial'. +weakTypeIntersections1.ts(23,1): error TS2769: No overload matches this call. + Overload 1 of 2, '(arg: () => Foo & ThisType): void', gave the following error. + Type 'number' is not assignable to type 'string'. + Overload 2 of 2, '(arg: Partial & ThisType): void', gave the following error. + Type '() => { a: number; }' has no properties in common with type 'Partial & ThisType'. + + +==== weakTypeIntersections1.ts (5 errors) ==== + interface EmptyInterface {} + interface EmptyInterface2 {} + + interface Foo { + a: string; + } + interface Bar { + b: string; + } + + const m1: Partial & ThisType<{ x: string }> = () => null; // error + ~~ +!!! error TS2559: Type '() => null' has no properties in common with type 'Partial & ThisType<{ x: string; }>'. + const m2: Partial = () => null; // error + ~~ +!!! error TS2559: Type '() => null' has no properties in common with type 'Partial'. + const m3: Partial & EmptyInterface = () => null; // error + ~~ +!!! error TS2559: Type '() => null' has no properties in common with type 'Partial & EmptyInterface'. + const m4: EmptyInterface & EmptyInterface2 = () => null; // ok + const m5: Partial & Partial = () => null; // error + ~~ +!!! error TS2559: Type '() => null' has no properties in common with type 'Partial & Partial'. + + // https://github.com/microsoft/TypeScript/issues/56995 + declare function fun0(arg: () => Foo & ThisType): void; + declare function fun0(arg: Partial & ThisType): void; + + fun0({ a: "1" }); // ok + fun0(() => ({ a: "1" })); // ok + fun0(() => ({ a: 1 })); // error + ~~~~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(arg: () => Foo & ThisType): void', gave the following error. +!!! error TS2769: Type 'number' is not assignable to type 'string'. +!!! error TS2769: Overload 2 of 2, '(arg: Partial & ThisType): void', gave the following error. +!!! error TS2769: Type '() => { a: number; }' has no properties in common with type 'Partial & ThisType'. +!!! related TS6500 weakTypeIntersections1.ts:5:3: The expected type comes from property 'a' which is declared here on type 'Foo & ThisType' \ No newline at end of file diff --git a/tests/baselines/reference/weakTypeIntersections1.symbols b/tests/baselines/reference/weakTypeIntersections1.symbols new file mode 100644 index 0000000000000..fa8b35025f3ff --- /dev/null +++ b/tests/baselines/reference/weakTypeIntersections1.symbols @@ -0,0 +1,80 @@ +//// [tests/cases/compiler/weakTypeIntersections1.ts] //// + +=== weakTypeIntersections1.ts === +interface EmptyInterface {} +>EmptyInterface : Symbol(EmptyInterface, Decl(weakTypeIntersections1.ts, 0, 0)) + +interface EmptyInterface2 {} +>EmptyInterface2 : Symbol(EmptyInterface2, Decl(weakTypeIntersections1.ts, 0, 27)) + +interface Foo { +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) + + a: string; +>a : Symbol(Foo.a, Decl(weakTypeIntersections1.ts, 3, 15)) +} +interface Bar { +>Bar : Symbol(Bar, Decl(weakTypeIntersections1.ts, 5, 1)) + + b: string; +>b : Symbol(Bar.b, Decl(weakTypeIntersections1.ts, 6, 15)) +} + +const m1: Partial & ThisType<{ x: string }> = () => null; // error +>m1 : Symbol(m1, Decl(weakTypeIntersections1.ts, 10, 5)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) +>ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(weakTypeIntersections1.ts, 10, 35)) + +const m2: Partial = () => null; // error +>m2 : Symbol(m2, Decl(weakTypeIntersections1.ts, 11, 5)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) + +const m3: Partial & EmptyInterface = () => null; // error +>m3 : Symbol(m3, Decl(weakTypeIntersections1.ts, 12, 5)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) +>EmptyInterface : Symbol(EmptyInterface, Decl(weakTypeIntersections1.ts, 0, 0)) + +const m4: EmptyInterface & EmptyInterface2 = () => null; // ok +>m4 : Symbol(m4, Decl(weakTypeIntersections1.ts, 13, 5)) +>EmptyInterface : Symbol(EmptyInterface, Decl(weakTypeIntersections1.ts, 0, 0)) +>EmptyInterface2 : Symbol(EmptyInterface2, Decl(weakTypeIntersections1.ts, 0, 27)) + +const m5: Partial & Partial = () => null; // error +>m5 : Symbol(m5, Decl(weakTypeIntersections1.ts, 14, 5)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Bar : Symbol(Bar, Decl(weakTypeIntersections1.ts, 5, 1)) + +// https://github.com/microsoft/TypeScript/issues/56995 +declare function fun0(arg: () => Foo & ThisType): void; +>fun0 : Symbol(fun0, Decl(weakTypeIntersections1.ts, 14, 51), Decl(weakTypeIntersections1.ts, 17, 60)) +>arg : Symbol(arg, Decl(weakTypeIntersections1.ts, 17, 22)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) +>ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) + +declare function fun0(arg: Partial & ThisType): void; +>fun0 : Symbol(fun0, Decl(weakTypeIntersections1.ts, 14, 51), Decl(weakTypeIntersections1.ts, 17, 60)) +>arg : Symbol(arg, Decl(weakTypeIntersections1.ts, 18, 22)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) +>ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --)) +>Foo : Symbol(Foo, Decl(weakTypeIntersections1.ts, 1, 28)) + +fun0({ a: "1" }); // ok +>fun0 : Symbol(fun0, Decl(weakTypeIntersections1.ts, 14, 51), Decl(weakTypeIntersections1.ts, 17, 60)) +>a : Symbol(a, Decl(weakTypeIntersections1.ts, 20, 6)) + +fun0(() => ({ a: "1" })); // ok +>fun0 : Symbol(fun0, Decl(weakTypeIntersections1.ts, 14, 51), Decl(weakTypeIntersections1.ts, 17, 60)) +>a : Symbol(a, Decl(weakTypeIntersections1.ts, 21, 13)) + +fun0(() => ({ a: 1 })); // error +>fun0 : Symbol(fun0, Decl(weakTypeIntersections1.ts, 14, 51), Decl(weakTypeIntersections1.ts, 17, 60)) +>a : Symbol(a, Decl(weakTypeIntersections1.ts, 22, 13)) + diff --git a/tests/baselines/reference/weakTypeIntersections1.types b/tests/baselines/reference/weakTypeIntersections1.types new file mode 100644 index 0000000000000..186a63df127b2 --- /dev/null +++ b/tests/baselines/reference/weakTypeIntersections1.types @@ -0,0 +1,106 @@ +//// [tests/cases/compiler/weakTypeIntersections1.ts] //// + +=== weakTypeIntersections1.ts === +interface EmptyInterface {} +interface EmptyInterface2 {} + +interface Foo { + a: string; +>a : string +> : ^^^^^^ +} +interface Bar { + b: string; +>b : string +> : ^^^^^^ +} + +const m1: Partial & ThisType<{ x: string }> = () => null; // error +>m1 : Partial & ThisType<{ x: string; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ +>x : string +> : ^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + +const m2: Partial = () => null; // error +>m2 : Partial +> : ^^^^^^^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + +const m3: Partial & EmptyInterface = () => null; // error +>m3 : Partial & EmptyInterface +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + +const m4: EmptyInterface & EmptyInterface2 = () => null; // ok +>m4 : EmptyInterface & EmptyInterface2 +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + +const m5: Partial & Partial = () => null; // error +>m5 : Partial & Partial +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + +// https://github.com/microsoft/TypeScript/issues/56995 +declare function fun0(arg: () => Foo & ThisType): void; +>fun0 : { (arg: () => Foo & ThisType): void; (arg: Partial & ThisType): void; } +> : ^^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^ +>arg : () => Foo & ThisType +> : ^^^^^^ + +declare function fun0(arg: Partial & ThisType): void; +>fun0 : { (arg: () => Foo & ThisType): void; (arg: Partial & ThisType): void; } +> : ^^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^ +>arg : Partial & ThisType +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +fun0({ a: "1" }); // ok +>fun0({ a: "1" }) : void +> : ^^^^ +>fun0 : { (arg: () => Foo & ThisType): void; (arg: Partial & ThisType): void; } +> : ^^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^ +>{ a: "1" } : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ +>"1" : "1" +> : ^^^ + +fun0(() => ({ a: "1" })); // ok +>fun0(() => ({ a: "1" })) : void +> : ^^^^ +>fun0 : { (arg: () => Foo & ThisType): void; (arg: Partial & ThisType): void; } +> : ^^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^ +>() => ({ a: "1" }) : () => { a: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>({ a: "1" }) : { a: string; } +> : ^^^^^^^^^^^^^^ +>{ a: "1" } : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ +>"1" : "1" +> : ^^^ + +fun0(() => ({ a: 1 })); // error +>fun0(() => ({ a: 1 })) : void +> : ^^^^ +>fun0 : { (arg: () => Foo & ThisType): void; (arg: Partial & ThisType): void; } +> : ^^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^ +>() => ({ a: 1 }) : () => { a: number; } +> : ^^^^^^^^^^^^^^^^^^^^ +>({ a: 1 }) : { a: number; } +> : ^^^^^^^^^^^^^^ +>{ a: 1 } : { a: number; } +> : ^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ + diff --git a/tests/cases/compiler/weakTypeIntersections1.ts b/tests/cases/compiler/weakTypeIntersections1.ts new file mode 100644 index 0000000000000..877a5bccf140a --- /dev/null +++ b/tests/cases/compiler/weakTypeIntersections1.ts @@ -0,0 +1,26 @@ +// @strict: true +// @noEmit: true + +interface EmptyInterface {} +interface EmptyInterface2 {} + +interface Foo { + a: string; +} +interface Bar { + b: string; +} + +const m1: Partial & ThisType<{ x: string }> = () => null; // error +const m2: Partial = () => null; // error +const m3: Partial & EmptyInterface = () => null; // error +const m4: EmptyInterface & EmptyInterface2 = () => null; // ok +const m5: Partial & Partial = () => null; // error + +// https://github.com/microsoft/TypeScript/issues/56995 +declare function fun0(arg: () => Foo & ThisType): void; +declare function fun0(arg: Partial & ThisType): void; + +fun0({ a: "1" }); // ok +fun0(() => ({ a: "1" })); // ok +fun0(() => ({ a: 1 })); // error \ No newline at end of file