Skip to content

Commit

Permalink
Allow trailing params of 'undefined', 'unknown', and 'any' to be opti…
Browse files Browse the repository at this point in the history
…onal in non-strictNullChecks JS (#40057)
  • Loading branch information
rbuckton committed Aug 20, 2020
1 parent 76639f1 commit 598e9b2
Show file tree
Hide file tree
Showing 8 changed files with 662 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -25745,6 +25745,10 @@ namespace ts {
return !!(t.flags & TypeFlags.Void);
}

function acceptsVoidUndefinedUnknownOrAny(t: Type): boolean {
return !!(t.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Unknown | TypeFlags.Any));
}

function hasCorrectArity(node: CallLikeExpression, args: readonly Expression[], signature: Signature, signatureHelpTrailingComma = false) {
let argCount: number;
let callIsIncomplete = false; // In incomplete call we want to be lenient when we have too few arguments
Expand Down Expand Up @@ -25810,7 +25814,7 @@ namespace ts {
}
for (let i = argCount; i < effectiveMinimumArguments; i++) {
const type = getTypeAtPosition(signature, i);
if (filterType(type, acceptsVoid).flags & TypeFlags.Never) {
if (filterType(type, isInJSFile(node) && !strictNullChecks ? acceptsVoidUndefinedUnknownOrAny : acceptsVoid).flags & TypeFlags.Never) {
return false;
}
}
Expand Down
@@ -0,0 +1,66 @@
tests/cases/conformance/expressions/functionCalls/tsfile.ts(6,1): error TS2554: Expected 1 arguments, but got 0.
tests/cases/conformance/expressions/functionCalls/tsfile.ts(7,1): error TS2554: Expected 1 arguments, but got 0.
tests/cases/conformance/expressions/functionCalls/tsfile.ts(8,1): error TS2554: Expected 1 arguments, but got 0.
tests/cases/conformance/expressions/functionCalls/tsfile.ts(10,4): error TS2554: Expected 1 arguments, but got 0.
tests/cases/conformance/expressions/functionCalls/tsfile.ts(11,4): error TS2554: Expected 1 arguments, but got 0.
tests/cases/conformance/expressions/functionCalls/tsfile.ts(12,4): error TS2554: Expected 1 arguments, but got 0.


==== tests/cases/conformance/expressions/functionCalls/defs.d.ts (0 errors) ====
declare function f1(p: void): void;
declare function f2(p: undefined): void;
declare function f3(p: unknown): void;
declare function f4(p: any): void;

interface I<T> { m(p: T): void; }
declare const o1: I<void>;
declare const o2: I<undefined>;
declare const o3: I<unknown>;
declare const o4: I<any>;

==== tests/cases/conformance/expressions/functionCalls/jsfile.js (0 errors) ====
// current behavior: treat trailing `void` as optional
f1();
o1.m();

// new behavior: treat 'undefined', 'unknown', and 'any' as optional in non-strict mode
f2();
f3();
f4();

o2.m();
o3.m();
o4.m();

==== tests/cases/conformance/expressions/functionCalls/tsfile.ts (6 errors) ====
// current behavior: treat trailing `void` as optional
f1();
o1.m();

// no change in behavior
f2();
~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:2:21: An argument for 'p' was not provided.
f3();
~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:3:21: An argument for 'p' was not provided.
f4();
~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:4:21: An argument for 'p' was not provided.

o2.m();
~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:6:20: An argument for 'p' was not provided.
o3.m();
~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:6:20: An argument for 'p' was not provided.
o4.m();
~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/defs.d.ts:6:20: An argument for 'p' was not provided.

@@ -0,0 +1,110 @@
=== tests/cases/conformance/expressions/functionCalls/defs.d.ts ===
declare function f1(p: void): void;
>f1 : Symbol(f1, Decl(defs.d.ts, 0, 0))
>p : Symbol(p, Decl(defs.d.ts, 0, 20))

declare function f2(p: undefined): void;
>f2 : Symbol(f2, Decl(defs.d.ts, 0, 35))
>p : Symbol(p, Decl(defs.d.ts, 1, 20))

declare function f3(p: unknown): void;
>f3 : Symbol(f3, Decl(defs.d.ts, 1, 40))
>p : Symbol(p, Decl(defs.d.ts, 2, 20))

declare function f4(p: any): void;
>f4 : Symbol(f4, Decl(defs.d.ts, 2, 38))
>p : Symbol(p, Decl(defs.d.ts, 3, 20))

interface I<T> { m(p: T): void; }
>I : Symbol(I, Decl(defs.d.ts, 3, 34))
>T : Symbol(T, Decl(defs.d.ts, 5, 12))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>p : Symbol(p, Decl(defs.d.ts, 5, 19))
>T : Symbol(T, Decl(defs.d.ts, 5, 12))

declare const o1: I<void>;
>o1 : Symbol(o1, Decl(defs.d.ts, 6, 13))
>I : Symbol(I, Decl(defs.d.ts, 3, 34))

declare const o2: I<undefined>;
>o2 : Symbol(o2, Decl(defs.d.ts, 7, 13))
>I : Symbol(I, Decl(defs.d.ts, 3, 34))

declare const o3: I<unknown>;
>o3 : Symbol(o3, Decl(defs.d.ts, 8, 13))
>I : Symbol(I, Decl(defs.d.ts, 3, 34))

declare const o4: I<any>;
>o4 : Symbol(o4, Decl(defs.d.ts, 9, 13))
>I : Symbol(I, Decl(defs.d.ts, 3, 34))

=== tests/cases/conformance/expressions/functionCalls/jsfile.js ===
// current behavior: treat trailing `void` as optional
f1();
>f1 : Symbol(f1, Decl(defs.d.ts, 0, 0))

o1.m();
>o1.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o1 : Symbol(o1, Decl(defs.d.ts, 6, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

// new behavior: treat 'undefined', 'unknown', and 'any' as optional in non-strict mode
f2();
>f2 : Symbol(f2, Decl(defs.d.ts, 0, 35))

f3();
>f3 : Symbol(f3, Decl(defs.d.ts, 1, 40))

f4();
>f4 : Symbol(f4, Decl(defs.d.ts, 2, 38))

o2.m();
>o2.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o2 : Symbol(o2, Decl(defs.d.ts, 7, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

o3.m();
>o3.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o3 : Symbol(o3, Decl(defs.d.ts, 8, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

o4.m();
>o4.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o4 : Symbol(o4, Decl(defs.d.ts, 9, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

=== tests/cases/conformance/expressions/functionCalls/tsfile.ts ===
// current behavior: treat trailing `void` as optional
f1();
>f1 : Symbol(f1, Decl(defs.d.ts, 0, 0))

o1.m();
>o1.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o1 : Symbol(o1, Decl(defs.d.ts, 6, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

// no change in behavior
f2();
>f2 : Symbol(f2, Decl(defs.d.ts, 0, 35))

f3();
>f3 : Symbol(f3, Decl(defs.d.ts, 1, 40))

f4();
>f4 : Symbol(f4, Decl(defs.d.ts, 2, 38))

o2.m();
>o2.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o2 : Symbol(o2, Decl(defs.d.ts, 7, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

o3.m();
>o3.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o3 : Symbol(o3, Decl(defs.d.ts, 8, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

o4.m();
>o4.m : Symbol(I.m, Decl(defs.d.ts, 5, 16))
>o4 : Symbol(o4, Decl(defs.d.ts, 9, 13))
>m : Symbol(I.m, Decl(defs.d.ts, 5, 16))

@@ -0,0 +1,119 @@
=== tests/cases/conformance/expressions/functionCalls/defs.d.ts ===
declare function f1(p: void): void;
>f1 : (p: void) => void
>p : void

declare function f2(p: undefined): void;
>f2 : (p: undefined) => void
>p : undefined

declare function f3(p: unknown): void;
>f3 : (p: unknown) => void
>p : unknown

declare function f4(p: any): void;
>f4 : (p: any) => void
>p : any

interface I<T> { m(p: T): void; }
>m : (p: T) => void
>p : T

declare const o1: I<void>;
>o1 : I<void>

declare const o2: I<undefined>;
>o2 : I<undefined>

declare const o3: I<unknown>;
>o3 : I<unknown>

declare const o4: I<any>;
>o4 : I<any>

=== tests/cases/conformance/expressions/functionCalls/jsfile.js ===
// current behavior: treat trailing `void` as optional
f1();
>f1() : void
>f1 : (p: void) => void

o1.m();
>o1.m() : void
>o1.m : (p: void) => void
>o1 : I<void>
>m : (p: void) => void

// new behavior: treat 'undefined', 'unknown', and 'any' as optional in non-strict mode
f2();
>f2() : void
>f2 : (p: undefined) => void

f3();
>f3() : void
>f3 : (p: unknown) => void

f4();
>f4() : void
>f4 : (p: any) => void

o2.m();
>o2.m() : void
>o2.m : (p: undefined) => void
>o2 : I<undefined>
>m : (p: undefined) => void

o3.m();
>o3.m() : void
>o3.m : (p: unknown) => void
>o3 : I<unknown>
>m : (p: unknown) => void

o4.m();
>o4.m() : void
>o4.m : (p: any) => void
>o4 : I<any>
>m : (p: any) => void

=== tests/cases/conformance/expressions/functionCalls/tsfile.ts ===
// current behavior: treat trailing `void` as optional
f1();
>f1() : void
>f1 : (p: void) => void

o1.m();
>o1.m() : void
>o1.m : (p: void) => void
>o1 : I<void>
>m : (p: void) => void

// no change in behavior
f2();
>f2() : void
>f2 : (p: undefined) => void

f3();
>f3() : void
>f3 : (p: unknown) => void

f4();
>f4() : void
>f4 : (p: any) => void

o2.m();
>o2.m() : void
>o2.m : (p: undefined) => void
>o2 : I<undefined>
>m : (p: undefined) => void

o3.m();
>o3.m() : void
>o3.m : (p: unknown) => void
>o3 : I<unknown>
>m : (p: unknown) => void

o4.m();
>o4.m() : void
>o4.m : (p: any) => void
>o4 : I<any>
>m : (p: any) => void

0 comments on commit 598e9b2

Please sign in to comment.