Skip to content

Commit d52b33f

Browse files
committed
Resolve instantiated object members through a shared worker
Eagerly instantiating an entire generic member table leaves millions of symbols alive even when checking only needs a few properties. Selective resolution saves memory, but separate lookup and enumeration algorithms would make inheritance, shadowing, and symbol identity harder to preserve. Named lookup, flags-only shape queries, and full enumeration need the same inheritance and shadowing rules. Cached member identities must stay stable when symbol types resolve later. Missing-property lookup must stay bounded in diamond-shaped hierarchies instead of exploring every path. Keeping preparation state after enumeration would waste the memory saved. Measured against upstream/main at aa81492 with six interleaved, fresh-process samples per version. The workload parses, binds, and checks the actual VS Code src/tsconfig.json with one checker, then measures live heap and heap scan bytes after GC while keeping the checker and program alive. No GC settings were changed. VS Code retained heap decreases 6.06%, heap scanning decreases 6.48%, and allocated bytes decrease 3.39%. Allocation events increase 2.83%; this is a real tradeoff, not an allocation-count improvement. Instruction medians decrease, but neither instructions nor elapsed time reach statistical significance on the shared machine, so this does not claim a CPU speedup. Exact benchstat output: goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ unified-main-bench.txt │ unified-final-bench.txt │ │ sec/op │ sec/op vs base │ ExternalProject-20 39.12 ± 11% 41.18 ± 8% ~ (p=0.240 n=6) │ unified-main-bench.txt │ unified-final-bench.txt │ │ diagnostics │ diagnostics vs base │ ExternalProject-20 321.0 ± 0% 321.0 ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ unified-main-bench.txt │ unified-final-bench.txt │ │ files │ files vs base │ ExternalProject-20 10.28k ± 0% 10.28k ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ unified-main-bench.txt │ unified-final-bench.txt │ │ retained-B │ retained-B vs base │ ExternalProject-20 4.996Gi ± 0% 4.693Gi ± 0% -6.06% (p=0.002 n=6) │ unified-main-bench.txt │ unified-final-bench.txt │ │ scan-B │ scan-B vs base │ ExternalProject-20 4.657Gi ± 0% 4.356Gi ± 0% -6.48% (p=0.002 n=6) │ unified-main-bench.txt │ unified-final-bench.txt │ │ symbols │ symbols vs base │ ExternalProject-20 6.385M ± 0% 4.154M ± 0% -34.94% (p=0.002 n=6) │ unified-main-bench.txt │ unified-final-bench.txt │ │ B/op │ B/op vs base │ ExternalProject-20 6.391Gi ± 0% 6.174Gi ± 0% -3.39% (p=0.002 n=6) │ unified-main-bench.txt │ unified-final-bench.txt │ │ allocs/op │ allocs/op vs base │ ExternalProject-20 32.61M ± 0% 33.54M ± 0% +2.83% (p=0.002 n=6) │ unified-main-instructions.txt │ unified-final-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalProject-20 322.1G ± 4% 308.8G ± 4% ~ (p=0.093 n=6) The smaller compiler fixture also saves retained heap and allocated bytes, with the same allocation-event tradeoff: │ unified-fixture-main-bench.txt │ unified-fixture-final-bench.txt │ │ retained-B │ retained-B vs base │ ExternalProject-20 217.2Mi ± 0% 212.9Mi ± 0% -1.99% (p=0.002 n=6) │ unified-fixture-main-bench.txt │ unified-fixture-final-bench.txt │ │ B/op │ B/op vs base │ ExternalProject-20 313.9Mi ± 0% 311.9Mi ± 0% -0.66% (p=0.002 n=6) │ unified-fixture-main-bench.txt │ unified-fixture-final-bench.txt │ │ allocs/op │ allocs/op vs base │ ExternalProject-20 1.515M ± 0% 1.536M ± 0% +1.41% (p=0.002 n=6) Compiler diagnostic, declaration, type, and symbol baselines match unchanged main. Coverage includes inheritance, polymorphic this, recursive defaults, reduced bases, index signatures, overlapping generic overloads, lookup-before-enumeration identity, table-key/name differences, negative diamond lookups, and hover/completion queries in either resolution state. The actual VS Code no-emit CLI output is byte-identical to main, including twelve pre-existing diagnostics and exit status 2. Validation passed: npx hereby validate go -C tsc test -race ./internal/checker -run 'TestInstantiated' -count=1 Task-only measurement helpers are not included in this change.
1 parent aa81492 commit d52b33f

9 files changed

Lines changed: 1543 additions & 45 deletions

‎tsc/internal/checker/checker.go‎

Lines changed: 248 additions & 43 deletions
Large diffs are not rendered by default.

‎tsc/internal/checker/checker_test.go‎

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package checker_test
22

33
import (
4+
"fmt"
45
"path/filepath"
6+
"strings"
57
"testing"
68

79
"github.com/microsoft/TypeScript/tsc/internal/ast"
@@ -115,6 +117,168 @@ export type E = D;`,
115117
assert.Equal(t, defaultClause, defaultReference)
116118
}
117119

120+
func TestInstantiatedMemberLookupIdentity(t *testing.T) {
121+
t.Parallel()
122+
for _, reverse := range []bool{false, true} {
123+
t.Run(core.IfElse(reverse, "reverse", "forward"), func(t *testing.T) {
124+
t.Parallel()
125+
fs := bundled.WrapFS(vfstest.FromMap(map[string]string{
126+
"/main.ts": `interface Base<T> { inherited: T; shared: string }
127+
interface Derived<T> extends Base<T[]> { own: T; shared: "derived"; unused: string }
128+
declare const instance: Derived<number>;
129+
instance.inherited;
130+
instance.own;
131+
instance.shared;`,
132+
"/tsconfig.json": `{"compilerOptions":{"strict":true},"files":["main.ts"]}`,
133+
}, tspath.CaseInsensitive))
134+
host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil)
135+
parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, fs, nil)
136+
assert.Equal(t, len(errors), 0)
137+
program := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host})
138+
program.BindSourceFiles()
139+
c, done := program.GetTypeChecker(t.Context())
140+
defer done()
141+
file := program.GetSourceFile("/main.ts")
142+
expressions := []*ast.Node{
143+
file.Statements.Nodes[3].Expression(),
144+
file.Statements.Nodes[4].Expression(),
145+
file.Statements.Nodes[5].Expression(),
146+
}
147+
symbols := make([]*ast.Symbol, len(expressions))
148+
typ := c.GetTypeAtLocation(expressions[0].Expression())
149+
for index := range expressions {
150+
i := index
151+
if reverse {
152+
i = len(expressions) - index - 1
153+
}
154+
symbols[i] = c.GetPropertyOfType(typ, expressions[i].Name().Text())
155+
assert.Assert(t, symbols[i] != nil)
156+
}
157+
before := c.SymbolCount
158+
for _, expression := range expressions {
159+
c.GetPropertyOfType(typ, expression.Name().Text())
160+
}
161+
assert.Equal(t, c.SymbolCount, before)
162+
unused := file.Statements.Nodes[1].Symbol().Members()["unused"]
163+
c.GetTypeOfSymbol(unused)
164+
properties := c.GetPropertiesOfType(typ)
165+
assert.Equal(t, len(properties), 4)
166+
assert.Equal(t, properties[0].Name(), "own")
167+
assert.Equal(t, properties[1].Name(), "shared")
168+
assert.Equal(t, properties[2].Name(), "unused")
169+
assert.Assert(t, properties[2] != unused)
170+
assert.Equal(t, properties[3].Name(), "inherited")
171+
for i, expression := range expressions {
172+
assert.Equal(t, c.GetPropertyOfType(typ, expression.Name().Text()), symbols[i])
173+
assert.Equal(t, c.GetSymbolAtLocation(expression), symbols[i])
174+
found := false
175+
for _, property := range properties {
176+
if property == symbols[i] {
177+
found = true
178+
}
179+
}
180+
assert.Assert(t, found)
181+
}
182+
assert.Equal(t, len(c.GetDiagnostics(t.Context(), file)), 0)
183+
})
184+
}
185+
}
186+
187+
func TestInstantiatedMemberShapeQueries(t *testing.T) {
188+
t.Parallel()
189+
fs := bundled.WrapFS(vfstest.FromMap(map[string]string{
190+
"/main.ts": `interface Container<T> {
191+
(): T;
192+
[key: string]: T;
193+
value: T;
194+
unused: T;
195+
another: T;
196+
}
197+
declare const container: Container<string>;
198+
container.value;`,
199+
"/tsconfig.json": `{"compilerOptions":{"strict":true},"files":["main.ts"]}`,
200+
}, tspath.CaseInsensitive))
201+
host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil)
202+
parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, fs, nil)
203+
assert.Equal(t, len(errors), 0)
204+
program := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host})
205+
program.BindSourceFiles()
206+
c, done := program.GetTypeChecker(t.Context())
207+
defer done()
208+
file := program.GetSourceFile("/main.ts")
209+
typ := c.GetTypeAtLocation(file.Statements.Nodes[2].Expression().Expression())
210+
before := c.SymbolCount
211+
value := c.GetPropertyOfType(typ, "value")
212+
assert.Assert(t, value != nil)
213+
assert.Equal(t, c.SymbolCount, before+1)
214+
before = c.SymbolCount
215+
assert.Equal(t, len(c.GetSignaturesOfType(typ, checker.SignatureKindCall)), 1)
216+
assert.Equal(t, len(c.GetIndexInfosOfType(typ)), 1)
217+
assert.Equal(t, c.SymbolCount, before)
218+
properties := c.GetPropertiesOfType(typ)
219+
assert.Equal(t, len(properties), 3)
220+
assert.Equal(t, c.SymbolCount, before+2)
221+
assert.Equal(t, c.GetPropertyOfType(typ, "value"), value)
222+
}
223+
224+
func TestInstantiatedMemberTableKeys(t *testing.T) {
225+
t.Parallel()
226+
fs := bundled.WrapFS(vfstest.FromMap(map[string]string{
227+
"/main.ts": `interface Base<T> { value: T }
228+
interface Derived<T> extends Base<T> {}
229+
declare const instance: Derived<string>;
230+
instance;`,
231+
"/tsconfig.json": `{"files":["main.ts"]}`,
232+
}, tspath.CaseInsensitive))
233+
host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil)
234+
parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, fs, nil)
235+
assert.Equal(t, len(errors), 0)
236+
program := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host})
237+
program.BindSourceFiles()
238+
file := program.GetSourceFile("/main.ts")
239+
members := file.Statements.Nodes[0].Symbol().Members()
240+
members["different"] = members["value"]
241+
delete(members, "value")
242+
c, done := program.GetTypeChecker(t.Context())
243+
defer done()
244+
typ := c.GetTypeAtLocation(file.Statements.Nodes[3].Expression())
245+
value := c.GetPropertyOfType(typ, "value")
246+
assert.Assert(t, value != nil)
247+
properties := c.GetPropertiesOfType(typ)
248+
assert.Equal(t, len(properties), 1)
249+
assert.Equal(t, properties[0], value)
250+
assert.Assert(t, c.GetPropertyOfType(typ, "different") == nil)
251+
}
252+
253+
func TestInstantiatedDiamondMissingMember(t *testing.T) {
254+
t.Parallel()
255+
var source strings.Builder
256+
source.WriteString("interface L0<T> { value?: T }\ninterface R0<T> { value?: T }\n")
257+
for i := 1; i <= 24; i++ {
258+
fmt.Fprintf(&source, "interface L%d<T> extends L%d<T>, R%d<T> {}\ninterface R%d<T> extends L%d<T>, R%d<T> {}\n", i, i-1, i-1, i, i-1, i-1)
259+
}
260+
source.WriteString("declare const instance: L24<string>;\ninstance;")
261+
fs := bundled.WrapFS(vfstest.FromMap(map[string]string{
262+
"/main.ts": source.String(),
263+
"/tsconfig.json": `{"files":["main.ts"]}`,
264+
}, tspath.CaseInsensitive))
265+
host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil)
266+
parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, fs, nil)
267+
assert.Equal(t, len(errors), 0)
268+
program := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host})
269+
program.BindSourceFiles()
270+
c, done := program.GetTypeChecker(t.Context())
271+
defer done()
272+
file := program.GetSourceFile("/main.ts")
273+
typ := c.GetTypeAtLocation(file.Statements.Nodes[len(file.Statements.Nodes)-1].Expression())
274+
assert.Assert(t, c.GetPropertyOfType(typ, "missing") == nil)
275+
value := c.GetPropertyOfType(typ, "value")
276+
assert.Assert(t, value != nil)
277+
properties := c.GetPropertiesOfType(typ)
278+
assert.Equal(t, len(properties), 1)
279+
assert.Equal(t, properties[0], value)
280+
}
281+
118282
func BenchmarkNewChecker(b *testing.B) {
119283
fs := bundled.WrapFS(osvfs.FS())
120284
rootPath := tspath.RootedDirectoryPathFromAbsolute(filepath.Join(repo.TestDataPath(), "fixtures/compiler"))

‎tsc/internal/checker/relater.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,8 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T
678678
// and no required properties, call/construct signatures or index signatures
679679
func (c *Checker) isWeakType(t *Type) bool {
680680
if t.flags&TypeFlagsObject != 0 {
681-
resolved := c.resolveStructuredTypeMembers(t)
682-
return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool {
681+
resolved := c.resolveStructuredTypeShape(t)
682+
return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && c.hasPropertiesOfStructuredType(t) && c.everyPropertyOfStructuredType(t, func(p *ast.Symbol) bool {
683683
return p.Flags()&ast.SymbolFlagsOptional != 0
684684
})
685685
}

‎tsc/internal/fourslash/tests/completionListForGenericInstance1_test.go‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,32 @@ i/**/`
3636
},
3737
})
3838
}
39+
40+
func TestGenericMemberLookupBeforeAndAfterCompletion(t *testing.T) {
41+
t.Parallel()
42+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
43+
const content = `
44+
interface Base<T> { inherited: T; shared: string }
45+
interface Derived<T> extends Base<T[]> { own: T; shared: "derived" }
46+
declare const instance: Derived<number>;
47+
instance./*inherited*/inherited;
48+
instance./*shared*/shared;
49+
instance./*completion*/
50+
`
51+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
52+
defer done()
53+
f.VerifyQuickInfoAt(t, "inherited", "(property) Base<number[]>.inherited: number[]", "")
54+
f.VerifyQuickInfoAt(t, "shared", `(property) Derived<number>.shared: "derived"`, "")
55+
f.VerifyCompletions(t, "completion", &fourslash.CompletionsExpectedList{
56+
IsIncomplete: false,
57+
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
58+
CommitCharacters: &DefaultCommitCharacters,
59+
EditRange: Ignored,
60+
},
61+
Items: &fourslash.CompletionsExpectedItems{
62+
Exact: []fourslash.CompletionsExpectedItem{"inherited", "own", "shared"},
63+
},
64+
})
65+
f.VerifyQuickInfoAt(t, "inherited", "(property) Base<number[]>.inherited: number[]", "")
66+
f.VerifyQuickInfoAt(t, "shared", `(property) Derived<number>.shared: "derived"`, "")
67+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
instantiatedReferenceMemberResolution.ts(14,9): error TS2339: Property 'missing' does not exist on type 'Derived<number>'.
2+
instantiatedReferenceMemberResolution.ts(28,5): error TS2341: Property 'secret' is private and only accessible within class 'Box<T>'.
3+
instantiatedReferenceMemberResolution.ts(34,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<string>'.
4+
instantiatedReferenceMemberResolution.ts(37,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'ConditionalBase<"b">'.
5+
instantiatedReferenceMemberResolution.ts(38,7): error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'ConditionalBase<"a">'.
6+
instantiatedReferenceMemberResolution.ts(43,9): error TS2339: Property 'bind' does not exist on type 'Fn<"b">'.
7+
instantiatedReferenceMemberResolution.ts(50,11): error TS2430: Interface 'Wrap<T>' incorrectly extends interface 'T'.
8+
'Wrap<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'.
9+
instantiatedReferenceMemberResolution.ts(51,7): error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'.
10+
instantiatedReferenceMemberResolution.ts(59,24): error TS2716: Type parameter 'T' has a circular default.
11+
instantiatedReferenceMemberResolution.ts(74,7): error TS2322: Type 'ReducedDict<"b">' is not assignable to type '{ [key: string]: number; }'.
12+
'string' index signatures are incompatible.
13+
Type 'unknown' is not assignable to type 'number'.
14+
instantiatedReferenceMemberResolution.ts(80,7): error TS2322: Type '<U>(x: U, y: string) => U' is not assignable to type 'number'.
15+
instantiatedReferenceMemberResolution.ts(82,7): error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'.
16+
17+
18+
==== instantiatedReferenceMemberResolution.ts (12 errors) ====
19+
interface Base<T> {
20+
value: T;
21+
shared: string;
22+
}
23+
interface Derived<T> extends Base<T[]> {
24+
shared: "derived";
25+
own: T;
26+
}
27+
declare const derived: Derived<number>;
28+
export const value = derived.value;
29+
export const shared = derived.shared;
30+
export const properties = { ...derived };
31+
export const again = derived.value;
32+
derived.missing;
33+
~~~~~~~
34+
!!! error TS2339: Property 'missing' does not exist on type 'Derived<number>'.
35+
36+
class Box<T> {
37+
private secret!: T;
38+
protected hidden!: T;
39+
contents!: T;
40+
get readonlyContents(): T { return this.contents; }
41+
set writeonlyContents(value: T) {}
42+
method<U>(value: U): [T, U] { return [this.contents, value]; }
43+
}
44+
declare const box: Box<string>;
45+
export const contents = box.contents;
46+
export const method = box.method(1);
47+
export const boxProperties = { ...box };
48+
box.secret;
49+
~~~~~~
50+
!!! error TS2341: Property 'secret' is private and only accessible within class 'Box<T>'.
51+
52+
interface Weak<T> {
53+
value?: T;
54+
}
55+
declare const unrelated: { other: number };
56+
const weak: Weak<string> = unrelated;
57+
~~~~
58+
!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<string>'.
59+
type Tagged<T> = { kind: T } & { kind: "a" };
60+
interface ConditionalBase<T extends string> extends Tagged<T> { optional?: T }
61+
const reduced: ConditionalBase<"b"> = unrelated;
62+
~~~~~~~
63+
!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'ConditionalBase<"b">'.
64+
const unreduced: ConditionalBase<"a"> = unrelated;
65+
~~~~~~~~~
66+
!!! error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'ConditionalBase<"a">'.
67+
!!! related TS2728 instantiatedReferenceMemberResolution.ts:35:20: 'kind' is declared here.
68+
69+
type Callable<T> = { (): T; kind: T } & { kind: "a" };
70+
interface Fn<T extends string> extends Callable<T> {}
71+
declare const neverFn: Fn<"b">;
72+
neverFn.bind;
73+
~~~~
74+
!!! error TS2339: Property 'bind' does not exist on type 'Fn<"b">'.
75+
declare const fn: Fn<"a">;
76+
export const bound = fn.bind;
77+
interface Dict<T> { [key: string]: T }
78+
declare const dict: Dict<string>;
79+
export const indexed = dict["property"];
80+
81+
interface Wrap<T extends { a?: string }> extends T {}
82+
~~~~
83+
!!! error TS2430: Interface 'Wrap<T>' incorrectly extends interface 'T'.
84+
!!! error TS2430: 'Wrap<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'.
85+
const wrapped: Wrap<{ a?: string; b: number }> = unrelated;
86+
~~~~~~~
87+
!!! error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'.
88+
!!! related TS2728 instantiatedReferenceMemberResolution.ts:51:35: 'b' is declared here.
89+
90+
interface Tree<T> extends Array<Tree<T>> { value: T }
91+
declare const tree: Tree<string>;
92+
export const children = tree.map(child => child.value);
93+
interface Self<T> { next: this; value: T }
94+
declare const self: Self<number>;
95+
export const next = self.next.next.value;
96+
interface Defaults<T = Defaults> { value: T }
97+
~~~~~~~~
98+
!!! error TS2716: Type parameter 'T' has a circular default.
99+
declare const defaults: Defaults;
100+
export const defaultValue = defaults.value;
101+
102+
interface First<T> { shared: T }
103+
interface Second<T> { shared: T; second: T }
104+
interface Multiple<T> extends First<T>, Second<T> { own: T }
105+
declare const multiple: Multiple<number>;
106+
export const multipleValue = multiple.shared;
107+
export const multipleProperties = { ...multiple };
108+
109+
interface ReducedDict<T extends string> extends Tagged<T> {
110+
[key: string]: unknown;
111+
}
112+
declare const reducedDict: ReducedDict<"b">;
113+
const numberDict: { [key: string]: number } = reducedDict;
114+
~~~~~~~~~~
115+
!!! error TS2322: Type 'ReducedDict<"b">' is not assignable to type '{ [key: string]: number; }'.
116+
!!! error TS2322: 'string' index signatures are incompatible.
117+
!!! error TS2322: Type 'unknown' is not assignable to type 'number'.
118+
119+
type Two<T> = { <U>(x: U, y: T): U } & { <U>(x: U, y: string): U };
120+
interface Overloads<T> extends Two<T> {}
121+
declare function pipe<A extends any[], B>(f: (...args: A) => B): (...args: A) => B;
122+
declare const merged: Overloads<string>;
123+
const piped1: number = pipe(merged);
124+
~~~~~~
125+
!!! error TS2322: Type '<U>(x: U, y: string) => U' is not assignable to type 'number'.
126+
declare const distinct: Overloads<number>;
127+
const piped2: number = pipe(distinct);
128+
~~~~~~
129+
!!! error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'.
130+
131+
interface CallableBox<T> { (): T }
132+
declare const boxOrCallable: CallableBox<string> | Box<string>;
133+
if (typeof boxOrCallable === "function") {
134+
boxOrCallable;
135+
}
136+

0 commit comments

Comments
 (0)