Skip to content

Commit 3bbbe28

Browse files
committed
Treat trailing 'void' as optional for assignability
1 parent 4aadd5a commit 3bbbe28

32 files changed

+364
-216
lines changed

Diff for: src/compiler/checker.ts

+42-13
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ namespace ts {
248248
ExportNamespace = 1 << 2,
249249
}
250250

251+
const enum MinArgumentCountFlags {
252+
None = 0,
253+
StrongArityForUntypedJS = 1 << 0,
254+
VoidIsNonOptional = 1 << 1,
255+
}
256+
251257
function SymbolLinks(this: SymbolLinks) {
252258
}
253259

@@ -9859,6 +9865,7 @@ namespace ts {
98599865
sig.resolvedReturnType = resolvedReturnType;
98609866
sig.resolvedTypePredicate = resolvedTypePredicate;
98619867
sig.minArgumentCount = minArgumentCount;
9868+
sig.resolvedMinArgumentCount = undefined;
98629869
sig.target = undefined;
98639870
sig.mapper = undefined;
98649871
sig.unionSignatures = undefined;
@@ -11318,7 +11325,10 @@ namespace ts {
1131811325
const signature = getSignatureFromDeclaration(node.parent);
1131911326
const parameterIndex = node.parent.parameters.indexOf(node);
1132011327
Debug.assert(parameterIndex >= 0);
11321-
return parameterIndex >= getMinArgumentCount(signature, /*strongArityForUntypedJS*/ true);
11328+
// Only consider syntactic or instantiated parameters as optional, not `void` parameters as this function is used
11329+
// in grammar checks and checking for `void` too early results in parameter types widening too early
11330+
// and causes some noImplicitAny errors to be lost.
11331+
return parameterIndex >= getMinArgumentCount(signature, MinArgumentCountFlags.StrongArityForUntypedJS | MinArgumentCountFlags.VoidIsNonOptional);
1132211332
}
1132311333
const iife = getImmediatelyInvokedFunctionExpression(node.parent);
1132411334
if (iife) {
@@ -28024,21 +28034,40 @@ namespace ts {
2802428034
return length;
2802528035
}
2802628036

28027-
function getMinArgumentCount(signature: Signature, strongArityForUntypedJS?: boolean) {
28028-
if (signatureHasRestParameter(signature)) {
28029-
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
28030-
if (isTupleType(restType)) {
28031-
const firstOptionalIndex = findIndex(restType.target.elementFlags, f => !(f & ElementFlags.Required));
28032-
const requiredCount = firstOptionalIndex < 0 ? restType.target.fixedLength : firstOptionalIndex;
28033-
if (requiredCount > 0) {
28034-
return signature.parameters.length - 1 + requiredCount;
28037+
function getMinArgumentCount(signature: Signature, flags?: MinArgumentCountFlags) {
28038+
const strongArityForUntypedJS = flags! & MinArgumentCountFlags.StrongArityForUntypedJS;
28039+
const voidIsNonOptional = flags! & MinArgumentCountFlags.VoidIsNonOptional;
28040+
if (voidIsNonOptional || signature.resolvedMinArgumentCount === undefined) {
28041+
let minArgumentCount: number | undefined;
28042+
if (signatureHasRestParameter(signature)) {
28043+
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
28044+
if (isTupleType(restType)) {
28045+
const firstOptionalIndex = findIndex(restType.target.elementFlags, f => !(f & ElementFlags.Required));
28046+
const requiredCount = firstOptionalIndex < 0 ? restType.target.fixedLength : firstOptionalIndex;
28047+
if (requiredCount > 0) {
28048+
minArgumentCount = signature.parameters.length - 1 + requiredCount;
28049+
}
2803528050
}
2803628051
}
28052+
if (minArgumentCount === undefined) {
28053+
if (!strongArityForUntypedJS && signature.flags & SignatureFlags.IsUntypedSignatureInJSFile) {
28054+
return 0;
28055+
}
28056+
minArgumentCount = signature.minArgumentCount;
28057+
}
28058+
if (voidIsNonOptional) {
28059+
return minArgumentCount;
28060+
}
28061+
for (let i = minArgumentCount - 1; i >= 0; i--) {
28062+
const type = getTypeAtPosition(signature, i);
28063+
if (filterType(type, acceptsVoid).flags & TypeFlags.Never) {
28064+
break;
28065+
}
28066+
minArgumentCount = i;
28067+
}
28068+
signature.resolvedMinArgumentCount = minArgumentCount;
2803728069
}
28038-
if (!strongArityForUntypedJS && signature.flags & SignatureFlags.IsUntypedSignatureInJSFile) {
28039-
return 0;
28040-
}
28041-
return signature.minArgumentCount;
28070+
return signature.resolvedMinArgumentCount;
2804228071
}
2804328072

2804428073
function hasEffectiveRestParameter(signature: Signature) {

Diff for: src/compiler/debug.ts

+41-4
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,25 @@ namespace ts {
413413
export function enableDebugInfo() {
414414
if (isDebugInfoEnabled) return;
415415

416+
// avoid recomputing
417+
let weakTypeTextMap: WeakMap<Type, string> | undefined;
418+
let weakNodeTextMap: WeakMap<Node, string> | undefined;
419+
420+
function getWeakTypeTextMap() {
421+
if (weakTypeTextMap === undefined) {
422+
if (typeof WeakMap === "function") weakTypeTextMap = new WeakMap();
423+
}
424+
return weakTypeTextMap;
425+
}
426+
427+
function getWeakNodeTextMap() {
428+
if (weakNodeTextMap === undefined) {
429+
if (typeof WeakMap === "function") weakNodeTextMap = new WeakMap();
430+
}
431+
return weakNodeTextMap;
432+
}
433+
434+
416435
// Add additional properties in debug mode to assist with debugging.
417436
Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
418437
__debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
@@ -421,7 +440,18 @@ namespace ts {
421440
Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
422441
__debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
423442
__debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((<ObjectType>this).objectFlags) : ""; } },
424-
__debugTypeToString: { value(this: Type) { return this.checker.typeToString(this); } },
443+
__debugTypeToString: {
444+
value(this: Type) {
445+
// avoid recomputing
446+
const map = getWeakTypeTextMap();
447+
let text = map?.get(this);
448+
if (text === undefined) {
449+
text = this.checker.typeToString(this);
450+
map?.set(this, text);
451+
}
452+
return text;
453+
}
454+
},
425455
});
426456

427457
const nodeConstructors = [
@@ -443,9 +473,16 @@ namespace ts {
443473
__debugGetText: {
444474
value(this: Node, includeTrivia?: boolean) {
445475
if (nodeIsSynthesized(this)) return "";
446-
const parseNode = getParseTreeNode(this);
447-
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
448-
return sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode!, includeTrivia) : "";
476+
// avoid recomputing
477+
const map = getWeakNodeTextMap();
478+
let text = map?.get(this);
479+
if (text === undefined) {
480+
const parseNode = getParseTreeNode(this);
481+
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
482+
text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode!, includeTrivia) : "";
483+
map?.set(this, text);
484+
}
485+
return text;
449486
}
450487
}
451488
});

Diff for: src/compiler/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5375,6 +5375,8 @@ namespace ts {
53755375
/* @internal */
53765376
minArgumentCount: number; // Number of non-optional parameters
53775377
/* @internal */
5378+
resolvedMinArgumentCount?: number; // Number of non-optional parameters (excluding trailing `void`)
5379+
/* @internal */
53785380
target?: Signature; // Instantiation target
53795381
/* @internal */
53805382
mapper?: TypeMapper; // Instantiation mapper

Diff for: src/lib/es2015.promise.d.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,18 @@ interface PromiseConstructor {
113113
*/
114114
reject<T = never>(reason?: any): Promise<T>;
115115

116+
/**
117+
* Creates a new resolved promise.
118+
* @returns A resolved promise.
119+
*/
120+
resolve(): Promise<void>;
121+
116122
/**
117123
* Creates a new resolved promise for the provided value.
118124
* @param value A promise.
119125
* @returns A promise whose internal state matches the provided promise.
120126
*/
121127
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
122-
123-
/**
124-
* Creates a new resolved promise .
125-
* @returns A resolved promise.
126-
*/
127-
resolve(): Promise<void>;
128128
}
129129

130130
declare var Promise: PromiseConstructor;

Diff for: tests/baselines/reference/arrayFrom.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const inputALike: ArrayLike<A> = { length: 0 };
2929
const inputARand = getEither(inputA, inputALike);
3030
>inputARand : ArrayLike<A> | Iterable<A>
3131
>getEither(inputA, inputALike) : ArrayLike<A> | Iterable<A>
32-
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => Iterable<T> | ArrayLike<T>
32+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<T>
3333
>inputA : A[]
3434
>inputALike : ArrayLike<A>
3535

@@ -161,12 +161,12 @@ const result11: B[] = Array.from(inputASet, ({ a }): B => ({ b: a }));
161161
// the ?: as always taking the false branch, narrowing to ArrayLike<T>,
162162
// even when the type is written as : Iterable<T>|ArrayLike<T>
163163
function getEither<T> (in1: Iterable<T>, in2: ArrayLike<T>) {
164-
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => Iterable<T> | ArrayLike<T>
164+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<T>
165165
>in1 : Iterable<T>
166166
>in2 : ArrayLike<T>
167167

168168
return Math.random() > 0.5 ? in1 : in2;
169-
>Math.random() > 0.5 ? in1 : in2 : Iterable<T> | ArrayLike<T>
169+
>Math.random() > 0.5 ? in1 : in2 : ArrayLike<T> | Iterable<T>
170170
>Math.random() > 0.5 : boolean
171171
>Math.random() : number
172172
>Math.random : () => number

Diff for: tests/baselines/reference/asyncArrowFunction11_es5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ class A {
1111
await Promise.resolve();
1212
>await Promise.resolve() : void
1313
>Promise.resolve() : Promise<void>
14-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
14+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
1515
>Promise : PromiseConstructor
16-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
16+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
1717

1818
const obj = { ["a"]: () => this }; // computed property name after `await` triggers case
1919
>obj : { a: () => this; }

Diff for: tests/baselines/reference/asyncFunctionReturnType.types

+20-20
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ async function fIndexedTypeForPromiseOfStringProp(obj: Obj): Promise<Obj["string
4444

4545
return Promise.resolve(obj.stringProp);
4646
>Promise.resolve(obj.stringProp) : Promise<string>
47-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
47+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
4848
>Promise : PromiseConstructor
49-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
49+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
5050
>obj.stringProp : string
5151
>obj : Obj
5252
>stringProp : string
@@ -58,9 +58,9 @@ async function fIndexedTypeForExplicitPromiseOfStringProp(obj: Obj): Promise<Obj
5858

5959
return Promise.resolve<Obj["stringProp"]>(obj.stringProp);
6060
>Promise.resolve<Obj["stringProp"]>(obj.stringProp) : Promise<string>
61-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
61+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
6262
>Promise : PromiseConstructor
63-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
63+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
6464
>obj.stringProp : string
6565
>obj : Obj
6666
>stringProp : string
@@ -82,9 +82,9 @@ async function fIndexedTypeForPromiseOfAnyProp(obj: Obj): Promise<Obj["anyProp"]
8282

8383
return Promise.resolve(obj.anyProp);
8484
>Promise.resolve(obj.anyProp) : Promise<any>
85-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
85+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
8686
>Promise : PromiseConstructor
87-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
87+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
8888
>obj.anyProp : any
8989
>obj : Obj
9090
>anyProp : any
@@ -96,9 +96,9 @@ async function fIndexedTypeForExplicitPromiseOfAnyProp(obj: Obj): Promise<Obj["a
9696

9797
return Promise.resolve<Obj["anyProp"]>(obj.anyProp);
9898
>Promise.resolve<Obj["anyProp"]>(obj.anyProp) : Promise<any>
99-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
99+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
100100
>Promise : PromiseConstructor
101-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
101+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
102102
>obj.anyProp : any
103103
>obj : Obj
104104
>anyProp : any
@@ -120,9 +120,9 @@ async function fGenericIndexedTypeForPromiseOfStringProp<TObj extends Obj>(obj:
120120

121121
return Promise.resolve(obj.stringProp);
122122
>Promise.resolve(obj.stringProp) : Promise<string>
123-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
123+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
124124
>Promise : PromiseConstructor
125-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
125+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
126126
>obj.stringProp : string
127127
>obj : TObj
128128
>stringProp : string
@@ -134,9 +134,9 @@ async function fGenericIndexedTypeForExplicitPromiseOfStringProp<TObj extends Ob
134134

135135
return Promise.resolve<TObj["stringProp"]>(obj.stringProp);
136136
>Promise.resolve<TObj["stringProp"]>(obj.stringProp) : Promise<TObj["stringProp"]>
137-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
137+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
138138
>Promise : PromiseConstructor
139-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
139+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
140140
>obj.stringProp : string
141141
>obj : TObj
142142
>stringProp : string
@@ -158,9 +158,9 @@ async function fGenericIndexedTypeForPromiseOfAnyProp<TObj extends Obj>(obj: TOb
158158

159159
return Promise.resolve(obj.anyProp);
160160
>Promise.resolve(obj.anyProp) : Promise<any>
161-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
161+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
162162
>Promise : PromiseConstructor
163-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
163+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
164164
>obj.anyProp : any
165165
>obj : TObj
166166
>anyProp : any
@@ -172,9 +172,9 @@ async function fGenericIndexedTypeForExplicitPromiseOfAnyProp<TObj extends Obj>(
172172

173173
return Promise.resolve<TObj["anyProp"]>(obj.anyProp);
174174
>Promise.resolve<TObj["anyProp"]>(obj.anyProp) : Promise<TObj["anyProp"]>
175-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
175+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
176176
>Promise : PromiseConstructor
177-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
177+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
178178
>obj.anyProp : any
179179
>obj : TObj
180180
>anyProp : any
@@ -198,9 +198,9 @@ async function fGenericIndexedTypeForPromiseOfKProp<TObj extends Obj, K extends
198198

199199
return Promise.resolve(obj[key]);
200200
>Promise.resolve(obj[key]) : Promise<TObj[K]>
201-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
201+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
202202
>Promise : PromiseConstructor
203-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
203+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
204204
>obj[key] : TObj[K]
205205
>obj : TObj
206206
>key : K
@@ -213,9 +213,9 @@ async function fGenericIndexedTypeForExplicitPromiseOfKProp<TObj extends Obj, K
213213

214214
return Promise.resolve<TObj[K]>(obj[key]);
215215
>Promise.resolve<TObj[K]>(obj[key]) : Promise<TObj[K]>
216-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
216+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
217217
>Promise : PromiseConstructor
218-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
218+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
219219
>obj[key] : TObj[K]
220220
>obj : TObj
221221
>key : K

Diff for: tests/baselines/reference/callWithMissingVoid.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(22,8):
44
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(35,31): error TS2554: Expected 1 arguments, but got 0.
55
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(36,35): error TS2554: Expected 1 arguments, but got 0.
66
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(37,33): error TS2554: Expected 1 arguments, but got 0.
7-
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(48,1): error TS2554: Expected 3 arguments, but got 1.
7+
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(48,1): error TS2554: Expected 2-3 arguments, but got 1.
88
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(55,1): error TS2554: Expected 4 arguments, but got 2.
99
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(56,1): error TS2554: Expected 4 arguments, but got 3.
1010
tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(57,1): error TS2554: Expected 4 arguments, but got 1.
@@ -79,7 +79,7 @@ tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts(75,1):
7979
a(4, "hello", void 0); // ok
8080
a(4); // not ok
8181
~~~~
82-
!!! error TS2554: Expected 3 arguments, but got 1.
82+
!!! error TS2554: Expected 2-3 arguments, but got 1.
8383
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithMissingVoid.ts:42:23: An argument for 'y' was not provided.
8484

8585
function b(x: number, y: string, z: void, what: number): void {

Diff for: tests/baselines/reference/exportDefaultAsyncFunction2.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ export default async(() => await(Promise.resolve(1)));
1919
>await(Promise.resolve(1)) : any
2020
>await : (...args: any[]) => any
2121
>Promise.resolve(1) : Promise<number>
22-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
22+
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
2323
>Promise : PromiseConstructor
24-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
24+
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
2525
>1 : 1
2626

2727
=== tests/cases/compiler/b.ts ===

0 commit comments

Comments
 (0)