Skip to content

Reuse type nodes from optional parameters even when not written as a union with undefined #48605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 8, 2022
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
22 changes: 12 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5915,9 +5915,6 @@ namespace ts {
if (parameterDeclaration && isRequiredInitializedParameter(parameterDeclaration)) {
parameterType = getOptionalType(parameterType);
}
if ((context.flags & NodeBuilderFlags.NoUndefinedOptionalParameterType) && parameterDeclaration && !isJSDocParameterTag(parameterDeclaration) && isOptionalUninitializedParameter(parameterDeclaration)) {
parameterType = getTypeWithFacts(parameterType, TypeFacts.NEUndefined);
}
const parameterTypeNode = serializeTypeForDeclaration(context, parameterType, parameterSymbol, context.enclosingDeclaration, privateSymbolVisitor, bundledImports);

const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && parameterDeclaration.modifiers ? parameterDeclaration.modifiers.map(factory.cloneNode) : undefined;
Expand Down Expand Up @@ -6544,7 +6541,7 @@ namespace ts {
if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation) && !isGetAccessorDeclaration(declWithExistingAnnotation)) {
// try to reuse the existing annotation
const existing = getEffectiveTypeAnnotationNode(declWithExistingAnnotation)!;
if (getTypeFromTypeNode(existing) === type && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type)) {
if (typeNodeIsEquivalentToType(existing, declWithExistingAnnotation, type) && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type)) {
const result = serializeExistingTypeNode(context, existing, includePrivateSymbol, bundled);
if (result) {
return result;
Expand All @@ -6562,6 +6559,17 @@ namespace ts {
return result;
}

function typeNodeIsEquivalentToType(typeNode: TypeNode, annotatedDeclaration: Declaration, type: Type) {
const typeFromTypeNode = getTypeFromTypeNode(typeNode);
if (typeFromTypeNode === type) {
return true;
}
if (isParameter(annotatedDeclaration) && annotatedDeclaration.questionToken) {
return getTypeWithFacts(type, TypeFacts.NEUndefined) === typeFromTypeNode;
}
return false;
}

function serializeReturnTypeForSignature(context: NodeBuilderContext, type: Type, signature: Signature, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) {
if (!isErrorType(type) && context.enclosingDeclaration) {
const annotation = signature.declaration && getEffectiveReturnTypeNode(signature.declaration);
Expand Down Expand Up @@ -42579,12 +42587,6 @@ namespace ts {
hasSyntacticModifier(parameter, ModifierFlags.ParameterPropertyModifier);
}

function isOptionalUninitializedParameter(parameter: ParameterDeclaration) {
return !!strictNullChecks &&
isOptionalParameter(parameter) &&
!parameter.initializer;
}

function isExpandoFunctionDeclaration(node: Declaration): boolean {
const declaration = getParseTreeNode(node, isFunctionDeclaration);
if (!declaration) {
Expand Down
1 change: 0 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4490,7 +4490,6 @@ namespace ts {
UseAliasDefinedOutsideCurrentScope = 1 << 14, // Allow non-visible aliases
UseSingleQuotesForStringLiteralType = 1 << 28, // Use single quotes for string literal type
NoTypeReduction = 1 << 29, // Don't call getReducedType
NoUndefinedOptionalParameterType = 1 << 30, // Do not add undefined to optional parameter type

// Error handling
AllowThisInObjectLiteral = 1 << 15,
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7659,4 +7659,8 @@ namespace ts {

return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined;
}

export function getParameterTypeNode(parameter: ParameterDeclaration | JSDocParameterTag) {
return parameter.kind === SyntaxKind.JSDocParameterTag ? parameter.typeExpression?.type : parameter.type;
}
}
1 change: 0 additions & 1 deletion src/services/codefixes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ namespace ts.codefix {
const scriptTarget = getEmitScriptTarget(program.getCompilerOptions());
const flags =
NodeBuilderFlags.NoTruncation
| NodeBuilderFlags.NoUndefinedOptionalParameterType
| NodeBuilderFlags.SuppressAnyReturnType
| NodeBuilderFlags.AllowEmptyTuple
| (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : NodeBuilderFlags.None);
Expand Down
1 change: 0 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2394,7 +2394,6 @@ declare namespace ts {
UseAliasDefinedOutsideCurrentScope = 16384,
UseSingleQuotesForStringLiteralType = 268435456,
NoTypeReduction = 536870912,
NoUndefinedOptionalParameterType = 1073741824,
AllowThisInObjectLiteral = 32768,
AllowQualifiedNameInPlaceOfIdentifier = 65536,
/** @deprecated AllowQualifedNameInPlaceOfIdentifier. Use AllowQualifiedNameInPlaceOfIdentifier instead. */
Expand Down
1 change: 0 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2394,7 +2394,6 @@ declare namespace ts {
UseAliasDefinedOutsideCurrentScope = 16384,
UseSingleQuotesForStringLiteralType = 268435456,
NoTypeReduction = 536870912,
NoUndefinedOptionalParameterType = 1073741824,
AllowThisInObjectLiteral = 32768,
AllowQualifiedNameInPlaceOfIdentifier = 65536,
/** @deprecated AllowQualifedNameInPlaceOfIdentifier. Use AllowQualifiedNameInPlaceOfIdentifier instead. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type BadFlatArray<Arr, Depth extends number> = {obj: {
>1 : 1

declare function flat<A, D extends number = 1>(
>flat : <A, D extends number = 1>(arr: A, depth?: D | undefined) => BadFlatArray<A, D>[]
>flat : <A, D extends number = 1>(arr: A, depth?: D) => BadFlatArray<A, D>[]

arr: A,
>arr : A
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/assertionTypePredicates1.types
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ namespace Debug {
>Debug : typeof Debug

export declare function assert(value: unknown, message?: string): asserts value;
>assert : (value: unknown, message?: string | undefined) => asserts value
>assert : (value: unknown, message?: string) => asserts value
>value : unknown
>message : string | undefined

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ declare function resolve2<T>(value: T): Windows.Foundation.IPromise<T>;
>Foundation : any

async function sample2(x?: number) {
>sample2 : (x?: number | undefined) => Promise<void>
>sample2 : (x?: number) => Promise<void>
>x : number | undefined

let x1 = await resolve1(x);
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/callsOnComplexSignatures.types
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function test3(items: string[] | number[]) {
}

function test4(
>test4 : (arg1: ((...objs: { x: number;}[]) => number) | ((...objs: { y: number;}[]) => number), arg2: ((a: { x: number;}, b: object) => number) | ((a: object, b: { x: number;}) => number), arg3: ((a: { x: number;}, ...objs: { y: number;}[]) => number) | ((...objs: { x: number;}[]) => number), arg4: ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number), arg5: ((a?: { x: number; } | undefined, ...b: { x: number;}[]) => number) | ((a?: { y: number; } | undefined) => number), arg6: ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number;}[]) => number)) => void
>test4 : (arg1: ((...objs: { x: number;}[]) => number) | ((...objs: { y: number;}[]) => number), arg2: ((a: { x: number;}, b: object) => number) | ((a: object, b: { x: number;}) => number), arg3: ((a: { x: number;}, ...objs: { y: number;}[]) => number) | ((...objs: { x: number;}[]) => number), arg4: ((a?: { x: number;}, b?: { x: number;}) => number) | ((a?: { y: number;}) => number), arg5: ((a?: { x: number;}, ...b: { x: number;}[]) => number) | ((a?: { y: number;}) => number), arg6: ((a?: { x: number;}, b?: { x: number;}) => number) | ((...a: { y: number;}[]) => number)) => void

arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number),
>arg1 : ((...objs: { x: number;}[]) => number) | ((...objs: { y: number;}[]) => number)
Expand Down Expand Up @@ -138,7 +138,7 @@ function test4(
>x : number

arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number),
>arg4 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number)
>arg4 : ((a?: { x: number;}, b?: { x: number;}) => number) | ((a?: { y: number;}) => number)
>a : { x: number; } | undefined
>x : number
>b : { x: number; } | undefined
Expand All @@ -147,7 +147,7 @@ function test4(
>y : number

arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number),
>arg5 : ((a?: { x: number; } | undefined, ...b: { x: number;}[]) => number) | ((a?: { y: number; } | undefined) => number)
>arg5 : ((a?: { x: number;}, ...b: { x: number;}[]) => number) | ((a?: { y: number;}) => number)
>a : { x: number; } | undefined
>x : number
>b : { x: number; }[]
Expand All @@ -156,7 +156,7 @@ function test4(
>y : number

arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number),
>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number;}[]) => number)
>arg6 : ((a?: { x: number;}, b?: { x: number;}) => number) | ((...a: { y: number;}[]) => number)
>a : { x: number; } | undefined
>x : number
>b : { x: number; } | undefined
Expand Down Expand Up @@ -339,7 +339,7 @@ function test5() {

// Pair of non-like intrinsics
function render(url?: string): React.ReactNode {
>render : (url?: string | undefined) => React.ReactNode
>render : (url?: string) => React.ReactNode
>url : string | undefined
>React : any

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/circularOptionalityRemoval.types
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function fn1(x: number | undefined = x > 0 ? x : 0) { }

// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }
>fn2 : (x?: string | undefined) => void
>fn2 : (x?: string) => void
>x : string | undefined
>someCondition ? 'value1' : x : string | undefined
>someCondition : any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ declare function id4<T extends (x: { foo?: number }) => any>(input: T): T;
>input : T

declare function id5<T extends (x?: number) => any>(input: T): T;
>id5 : <T extends (x?: number | undefined) => any>(input: T) => T
>id5 : <T extends (x?: number) => any>(input: T) => T
>x : number | undefined
>input : T

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowAliasing.types
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ function f27(outer: { obj: { kind: 'foo', foo: string } | { kind: 'bar', bar: nu
}

function f28(obj?: { kind: 'foo', foo: string } | { kind: 'bar', bar: number }) {
>f28 : (obj?: { kind: 'foo'; foo: string; } | { kind: 'bar'; bar: number; } | undefined) => void
>f28 : (obj?: { kind: 'foo'; foo: string;} | { kind: 'bar'; bar: number;}) => void
>obj : { kind: 'foo'; foo: string; } | { kind: 'bar'; bar: number; } | undefined
>kind : "foo"
>foo : string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const boo: Foo = { bar: 'bar' };
>'bar' : "bar"

function a(aboo1?: Foo) {
>a : (aboo1?: Foo | undefined) => void
>a : (aboo1?: Foo) => void
>aboo1 : Foo | undefined

if (!aboo1) return;
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowOptionalChain.types
Original file line number Diff line number Diff line change
Expand Up @@ -1882,7 +1882,7 @@ type Shape =
>radius : number

function getArea(shape?: Shape) {
>getArea : (shape?: Shape | undefined) => number
>getArea : (shape?: Shape) => number
>shape : Shape | undefined

switch (shape?.type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts ===
function f(addUndefined1 = "J", addUndefined2?: number) {
>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number
>f : (addUndefined1?: string, addUndefined2?: number) => number
>addUndefined1 : string
>"J" : "J"
>addUndefined2 : number | undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/destructureOptionalParameter.ts ===
declare function f1({ a, b }?: { a: number, b: string }): void;
>f1 : ({ a, b }?: { a: number; b: string; } | undefined) => void
>f1 : ({ a, b }?: { a: number; b: string;}) => void
>a : number
>b : string
>a : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function f1(options?: { color?: string, width?: number }) {
}

function f2(options?: [string?, number?]) {
>f2 : (options?: [(string | undefined)?, (number | undefined)?] | undefined) => void
>f2 : (options?: [string?, number?]) => void
>options : [(string | undefined)?, (number | undefined)?] | undefined

let [str, num] = options || [];
Expand Down Expand Up @@ -133,7 +133,7 @@ function f3(options?: { color: string, width: number }) {
}

function f4(options?: [string, number]) {
>f4 : (options?: [string, number] | undefined) => void
>f4 : (options?: [string, number]) => void
>options : [string, number] | undefined

let [str, num] = options || [];
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/genericFunctionInference1.types
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ const fn20 = pipe((_a?: {}) => 1);
>fn20 : (_a?: {} | undefined) => number
>pipe((_a?: {}) => 1) : (_a?: {} | undefined) => number
>pipe : { <A extends any[], B>(ab: (...args: A) => B): (...args: A) => B; <A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C; <A extends any[], B, C, D>(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D; }
>(_a?: {}) => 1 : (_a?: {} | undefined) => number
>(_a?: {}) => 1 : (_a?: {}) => number
>_a : {} | undefined
>1 : 1

Expand Down Expand Up @@ -968,7 +968,7 @@ const fn62 = pipe(
// Repro from #30297

declare function foo2<T, U = T>(fn: T, a?: U, b?: U): [T, U];
>foo2 : <T, U = T>(fn: T, a?: U | undefined, b?: U | undefined) => [T, U]
>foo2 : <T, U = T>(fn: T, a?: U, b?: U) => [T, U]
>fn : T
>a : U | undefined
>b : U | undefined
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/genericRestParameters1.types
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ f23();
>f23 : () => string[]

declare const g20: (x: number, y?: string, z?: boolean) => string[];
>g20 : (x: number, y?: string | undefined, z?: boolean | undefined) => string[]
>g20 : (x: number, y?: string, z?: boolean) => string[]
>x : number
>y : string | undefined
>z : boolean | undefined
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/indexedAccessNormalization.types
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type MyMap<M extends object> = {
}

declare function g<T>(value?: T): void;
>g : <T>(value?: T | undefined) => void
>g : <T>(value?: T) => void
>value : T | undefined

function f1<M extends object>(mymap: MyMap<M>, k: keyof M) {
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/inferFromBindingPattern.types
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface Person {
}

declare function selectJohn<K = Person>(props?: SelectProps<Person, K>): SelectResult<Person, K>;
>selectJohn : <K = Person>(props?: SelectProps<Person, K> | undefined) => SelectResult<Person, K>
>selectJohn : <K = Person>(props?: SelectProps<Person, K>) => SelectResult<Person, K>
>props : SelectProps<Person, K> | undefined

const [person] = selectJohn();
Expand Down Expand Up @@ -91,7 +91,7 @@ declare function makeTuple<T1>(arg: T1): [T1];
>arg : T1

declare function stringy<T = string>(arg?: T): T;
>stringy : <T = string>(arg?: T | undefined) => T
>stringy : <T = string>(arg?: T) => T
>arg : T | undefined

const isStringTuple = makeTuple(stringy()); // [string]
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/inferenceErasedSignatures.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract class SomeAbstractClass<C, M, R> extends SomeBaseClass {
>SomeBaseClass : SomeBaseClass

foo!: (r?: R) => void;
>foo : (r?: R | undefined) => void
>foo : (r?: R) => void
>r : R | undefined

bar!: (r?: any) => void;
Expand Down Expand Up @@ -72,7 +72,7 @@ interface BaseType<T1, T2> {
>c : T1

useT2(r?: T2): void;
>useT2 : (r?: T2 | undefined) => void
>useT2 : (r?: T2) => void
>r : T2 | undefined

unrelatedButSomehowRelevant(r?: any): void;
Expand All @@ -99,7 +99,7 @@ interface StructuralVersion {
>c : number

useT2(r?: boolean): void;
>useT2 : (r?: boolean | undefined) => void
>useT2 : (r?: boolean) => void
>r : boolean | undefined

unrelatedButSomehowRelevant(r?: any): void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
=== tests/cases/compiler/index.d.ts ===
export declare function foo({a}?: {
>foo : ({ a }?: { a?: string | undefined; } | undefined) => void
>foo : ({ a }?: { a?: string;}) => void
>a : string | undefined

a?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class Foo {
}

private async bar(node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {}) {
>bar : (node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {} | undefined) => Promise<undefined>
>bar : (node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: {}) => Promise<undefined>
>node : CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode
>options : {} | undefined

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/jsDeclarationsReactComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ declare const TabbedShowLayout: {
};
} & ((props?: {
elem: string;
} | undefined) => JSX.Element);
}) => JSX.Element);
//// [jsDeclarationsReactComponents4.d.ts]
export default TabbedShowLayout;
declare function TabbedShowLayout(prop: {
Expand Down
Loading