Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10785,7 +10785,10 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node)
hasSignatures = hasSignatures || len(resolved.CallSignatures()) != 0 || len(resolved.ConstructSignatures()) != 0
hasApplicableSignature = hasApplicableSignature || len(callSignatures) != 0 || len(constructSignatures) != 0
if !core.Same(callSignatures, resolved.CallSignatures()) || !core.Same(constructSignatures, resolved.ConstructSignatures()) {
result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, t.symbol)
symbol := c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameInstantiationExpression)
debug.Assert(t.symbol != nil, "Instantiation expression source type must have a symbol")
symbol.Declarations = t.symbol.Declarations
result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, symbol)
c.setStructuredTypeMembers(result, resolved.members, callSignatures, constructSignatures, resolved.indexInfos)
result.AsInstantiationExpressionType().node = node
return result
Expand Down
21 changes: 19 additions & 2 deletions tsc/internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,25 @@ func CompareTypes(t1, t2 *Type) int {
case t1.flags&(TypeFlagsAny|TypeFlagsUnknown|TypeFlagsString|TypeFlagsNumber|TypeFlagsBoolean|TypeFlagsBigInt|TypeFlagsESSymbol|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsNever|TypeFlagsNonPrimitive) != 0:
// Only distinguished by type IDs, handled below.
case t1.flags&TypeFlagsObject != 0:
// Order unnamed or identically named object types by symbol.
if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 {
// Order instantiation expression types without relying on lazy symbol IDs.
// Order other unnamed or identically named object types by symbol.
if t1.objectFlags&ObjectFlagsInstantiationExpressionType != 0 && t2.objectFlags&ObjectFlagsInstantiationExpressionType != 0 {
var declaration1, declaration2 *ast.Node
if t1.symbol != nil && len(t1.symbol.Declarations) != 0 {
declaration1 = t1.symbol.Declarations[0]
Comment on lines +445 to +446

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Declarations stable between every type-checker?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that should be.

}
if t2.symbol != nil && len(t2.symbol.Declarations) != 0 {
declaration2 = t2.symbol.Declarations[0]
}
// A single instantiation expression can produce multiple types for union constituents,
// so compare their source declarations before comparing the shared expression node.
if c := t1.checker.compareNodes(declaration1, declaration2); c != 0 {
return c
}
if c := t1.checker.compareNodes(t1.AsInstantiationExpressionType().node, t2.AsInstantiationExpressionType().node); c != 0 {
return c
}
} else if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 {
return c
}
// When object types have the same or no symbol, order by kind. We order type references before other kinds.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
classExtendsInstantiationExpressionType.ts(16,13): error TS2345: Argument of type '"foo3"' is not assignable to parameter of type '"foo" | "foo2"'.
classExtendsInstantiationExpressionType.ts(21,13): error TS2345: Argument of type '"foo3"' is not assignable to parameter of type '"foo" | "foo2"'.


==== classExtendsInstantiationExpressionType.ts (2 errors) ====
// Regression test for https://github.com/microsoft/TypeScript/issues/64116
declare function createComponent<Props extends {}, Events extends {}>(
render: { props: Props, events: Events }
): typeof Component<Events>;

declare class Component<Events extends {} = Record<string, any>> {
$on<K extends keyof Events>(event: K, handler: (e: Events[K]) => void): void;
}

const B = createComponent({
props: {},
events: { foo: "", foo2: "2" }
});

new B().$on("foo", e => {});
new B().$on("foo3", e => {});
~~~~~~
!!! error TS2345: Argument of type '"foo3"' is not assignable to parameter of type '"foo" | "foo2"'.

class C extends B {}

new C().$on("foo", e => {});
new C().$on("foo3", e => {});
~~~~~~
!!! error TS2345: Argument of type '"foo3"' is not assignable to parameter of type '"foo" | "foo2"'.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//// [tests/cases/compiler/classExtendsInstantiationExpressionType.ts] ////

=== classExtendsInstantiationExpressionType.ts ===
// Regression test for https://github.com/microsoft/TypeScript/issues/64116
declare function createComponent<Props extends {}, Events extends {}>(
>createComponent : Symbol(createComponent, Decl(classExtendsInstantiationExpressionType.ts, 0, 0))
>Props : Symbol(Props, Decl(classExtendsInstantiationExpressionType.ts, 1, 33))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 1, 50))

render: { props: Props, events: Events }
>render : Symbol(render, Decl(classExtendsInstantiationExpressionType.ts, 1, 70))
>props : Symbol(props, Decl(classExtendsInstantiationExpressionType.ts, 2, 13))
>Props : Symbol(Props, Decl(classExtendsInstantiationExpressionType.ts, 1, 33))
>events : Symbol(events, Decl(classExtendsInstantiationExpressionType.ts, 2, 27))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 1, 50))

): typeof Component<Events>;
>Component : Symbol(Component, Decl(classExtendsInstantiationExpressionType.ts, 3, 28))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 1, 50))

declare class Component<Events extends {} = Record<string, any>> {
>Component : Symbol(Component, Decl(classExtendsInstantiationExpressionType.ts, 3, 28))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 5, 24))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))

$on<K extends keyof Events>(event: K, handler: (e: Events[K]) => void): void;
>$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>K : Symbol(K, Decl(classExtendsInstantiationExpressionType.ts, 6, 8))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 5, 24))
>event : Symbol(event, Decl(classExtendsInstantiationExpressionType.ts, 6, 32))
>K : Symbol(K, Decl(classExtendsInstantiationExpressionType.ts, 6, 8))
>handler : Symbol(handler, Decl(classExtendsInstantiationExpressionType.ts, 6, 41))
>e : Symbol(e, Decl(classExtendsInstantiationExpressionType.ts, 6, 52))
>Events : Symbol(Events, Decl(classExtendsInstantiationExpressionType.ts, 5, 24))
>K : Symbol(K, Decl(classExtendsInstantiationExpressionType.ts, 6, 8))
}

const B = createComponent({
>B : Symbol(B, Decl(classExtendsInstantiationExpressionType.ts, 9, 5))
>createComponent : Symbol(createComponent, Decl(classExtendsInstantiationExpressionType.ts, 0, 0))

props: {},
>props : Symbol(props, Decl(classExtendsInstantiationExpressionType.ts, 9, 27))

events: { foo: "", foo2: "2" }
>events : Symbol(events, Decl(classExtendsInstantiationExpressionType.ts, 10, 14))
>foo : Symbol(foo, Decl(classExtendsInstantiationExpressionType.ts, 11, 13))
>foo2 : Symbol(foo2, Decl(classExtendsInstantiationExpressionType.ts, 11, 22))

});

new B().$on("foo", e => {});
>new B().$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>B : Symbol(B, Decl(classExtendsInstantiationExpressionType.ts, 9, 5))
>$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>e : Symbol(e, Decl(classExtendsInstantiationExpressionType.ts, 14, 18))

new B().$on("foo3", e => {});
>new B().$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>B : Symbol(B, Decl(classExtendsInstantiationExpressionType.ts, 9, 5))
>$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>e : Symbol(e, Decl(classExtendsInstantiationExpressionType.ts, 15, 19))

class C extends B {}
>C : Symbol(C, Decl(classExtendsInstantiationExpressionType.ts, 15, 29))
>B : Symbol(B, Decl(classExtendsInstantiationExpressionType.ts, 9, 5))

new C().$on("foo", e => {});
>new C().$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>C : Symbol(C, Decl(classExtendsInstantiationExpressionType.ts, 15, 29))
>$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>e : Symbol(e, Decl(classExtendsInstantiationExpressionType.ts, 19, 18))

new C().$on("foo3", e => {});
>new C().$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>C : Symbol(C, Decl(classExtendsInstantiationExpressionType.ts, 15, 29))
>$on : Symbol(Component.$on, Decl(classExtendsInstantiationExpressionType.ts, 5, 66))
>e : Symbol(e, Decl(classExtendsInstantiationExpressionType.ts, 20, 19))

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//// [tests/cases/compiler/classExtendsInstantiationExpressionType.ts] ////

=== classExtendsInstantiationExpressionType.ts ===
// Regression test for https://github.com/microsoft/TypeScript/issues/64116
declare function createComponent<Props extends {}, Events extends {}>(
>createComponent : <Props extends {}, Events extends {}>(render: { props: Props; events: Events; }) => typeof Component<Events>

render: { props: Props, events: Events }
>render : { props: Props; events: Events; }
>props : Props
>events : Events

): typeof Component<Events>;
>Component : typeof Component

declare class Component<Events extends {} = Record<string, any>> {
>Component : Component<Events>

$on<K extends keyof Events>(event: K, handler: (e: Events[K]) => void): void;
>$on : <K extends keyof Events>(event: K, handler: (e: Events[K]) => void) => void
>event : K
>handler : (e: Events[K]) => void
>e : Events[K]
}

const B = createComponent({
>B : { new (): Component<{ foo: string; foo2: string; }>; prototype: Component<any>; }
>createComponent({ props: {}, events: { foo: "", foo2: "2" }}) : { new (): Component<{ foo: string; foo2: string; }>; prototype: Component<any>; }
>createComponent : <Props extends {}, Events extends {}>(render: { props: Props; events: Events; }) => typeof Component<Events>
>{ props: {}, events: { foo: "", foo2: "2" }} : { props: {}; events: { foo: string; foo2: string; }; }

props: {},
>props : {}
>{} : {}

events: { foo: "", foo2: "2" }
>events : { foo: string; foo2: string; }
>{ foo: "", foo2: "2" } : { foo: string; foo2: string; }
>foo : string
>"" : ""
>foo2 : string
>"2" : "2"

});

new B().$on("foo", e => {});
>new B().$on("foo", e => {}) : void
>new B().$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>new B() : Component<{ foo: string; foo2: string; }>
>B : { new (): Component<{ foo: string; foo2: string; }>; prototype: Component<any>; }
>$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>"foo" : "foo"
>e => {} : (e: string) => void
>e : string

new B().$on("foo3", e => {});
>new B().$on("foo3", e => {}) : void
>new B().$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>new B() : Component<{ foo: string; foo2: string; }>
>B : { new (): Component<{ foo: string; foo2: string; }>; prototype: Component<any>; }
>$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>"foo3" : "foo3"
>e => {} : (e: string) => void
>e : string

class C extends B {}
>C : C
>B : Component<{ foo: string; foo2: string; }>

new C().$on("foo", e => {});
>new C().$on("foo", e => {}) : void
>new C().$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>new C() : C
>C : typeof C
>$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>"foo" : "foo"
>e => {} : (e: string) => void
>e : string

new C().$on("foo3", e => {});
>new C().$on("foo3", e => {}) : void
>new C().$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>new C() : C
>C : typeof C
>$on : <K extends "foo" | "foo2">(event: K, handler: (e: { foo: string; foo2: string; }[K]) => void) => void
>"foo3" : "foo3"
>e => {} : (e: string) => void
>e : string

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @noEmit: true

// Regression test for https://github.com/microsoft/TypeScript/issues/64116
declare function createComponent<Props extends {}, Events extends {}>(
render: { props: Props, events: Events }
): typeof Component<Events>;

declare class Component<Events extends {} = Record<string, any>> {
$on<K extends keyof Events>(event: K, handler: (e: Events[K]) => void): void;
}

const B = createComponent({
props: {},
events: { foo: "", foo2: "2" }
});

new B().$on("foo", e => {});
new B().$on("foo3", e => {});

class C extends B {}

new C().$on("foo", e => {});
new C().$on("foo3", e => {});