mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
fix(49080): --exactOptionalPropertyTypes seems to be ignored for JSDoc types (#49910)
* fix(49080): handle JSDocPropertyTag as optional prop * move condition to utility function isOptionalDeclaration * add additional tests * update baseline
This commit is contained in:
parent
355991c806
commit
708a522ddf
@ -535,9 +535,11 @@ import {
|
||||
isJSDocNode,
|
||||
isJSDocNonNullableType,
|
||||
isJSDocNullableType,
|
||||
isJSDocOptionalParameter,
|
||||
isJSDocOptionalType,
|
||||
isJSDocParameterTag,
|
||||
isJSDocPropertyLikeTag,
|
||||
isJSDocPropertyTag,
|
||||
isJSDocReturnTag,
|
||||
isJSDocSignature,
|
||||
isJSDocTemplateTag,
|
||||
@ -599,6 +601,7 @@ import {
|
||||
isOmittedExpression,
|
||||
isOptionalChain,
|
||||
isOptionalChainRoot,
|
||||
isOptionalDeclaration,
|
||||
isOptionalJSDocPropertyLikeTag,
|
||||
isOptionalTypeNode,
|
||||
isOutermostOptionalChain,
|
||||
@ -10164,11 +10167,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return getTypeForBindingElement(declaration as BindingElement);
|
||||
}
|
||||
|
||||
const isProperty = isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration) || isPropertySignature(declaration);
|
||||
const isOptional = includeOptionality && (
|
||||
isProperty && !!declaration.questionToken ||
|
||||
isParameter(declaration) && (!!declaration.questionToken || isJSDocOptionalParameter(declaration)) ||
|
||||
isOptionalJSDocPropertyLikeTag(declaration));
|
||||
const isProperty = (isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration)) || isPropertySignature(declaration) || isJSDocPropertyTag(declaration);
|
||||
const isOptional = includeOptionality && isOptionalDeclaration(declaration);
|
||||
|
||||
// Use type from type annotation if one is present
|
||||
const declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
|
||||
@ -13991,14 +13991,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return result;
|
||||
}
|
||||
|
||||
function isJSDocOptionalParameter(node: ParameterDeclaration) {
|
||||
return isInJSFile(node) && (
|
||||
// node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType
|
||||
node.type && node.type.kind === SyntaxKind.JSDocOptionalType
|
||||
|| getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) =>
|
||||
isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
|
||||
}
|
||||
|
||||
function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) {
|
||||
if (isExternalModuleNameRelative(moduleName)) {
|
||||
return undefined;
|
||||
|
||||
@ -414,6 +414,7 @@ import {
|
||||
PropertyDeclaration,
|
||||
PropertyName,
|
||||
PropertyNameLiteral,
|
||||
PropertySignature,
|
||||
PseudoBigInt,
|
||||
QualifiedName,
|
||||
ReadonlyCollection,
|
||||
@ -9139,3 +9140,26 @@ export function canUsePropertyAccess(name: string, languageVersion: ScriptTarget
|
||||
export function hasTabstop(node: Node): boolean {
|
||||
return getSnippetElement(node)?.kind === SnippetKind.TabStop;
|
||||
}
|
||||
|
||||
export function isJSDocOptionalParameter(node: ParameterDeclaration) {
|
||||
return isInJSFile(node) && (
|
||||
// node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType
|
||||
node.type && node.type.kind === SyntaxKind.JSDocOptionalType
|
||||
|| getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) =>
|
||||
isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
|
||||
}
|
||||
|
||||
export function isOptionalDeclaration(declaration: Declaration): boolean {
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return !!(declaration as PropertyDeclaration | PropertySignature).questionToken;
|
||||
case SyntaxKind.Parameter:
|
||||
return !!(declaration as ParameterDeclaration).questionToken || isJSDocOptionalParameter(declaration as ParameterDeclaration);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
return isOptionalJSDocPropertyLikeTag(declaration);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8766,6 +8766,8 @@ declare namespace ts {
|
||||
parent: ConstructorDeclaration;
|
||||
name: Identifier;
|
||||
};
|
||||
function isJSDocOptionalParameter(node: ParameterDeclaration): boolean;
|
||||
function isOptionalDeclaration(declaration: Declaration): boolean;
|
||||
function createUnparsedSourceFile(text: string): UnparsedSource;
|
||||
function createUnparsedSourceFile(inputFile: InputFiles, type: "js" | "dts", stripInternal?: boolean): UnparsedSource;
|
||||
function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
|
||||
|
||||
@ -4832,6 +4832,8 @@ declare namespace ts {
|
||||
parent: ConstructorDeclaration;
|
||||
name: Identifier;
|
||||
};
|
||||
function isJSDocOptionalParameter(node: ParameterDeclaration): boolean;
|
||||
function isOptionalDeclaration(declaration: Declaration): boolean;
|
||||
function createUnparsedSourceFile(text: string): UnparsedSource;
|
||||
function createUnparsedSourceFile(inputFile: InputFiles, type: "js" | "dts", stripInternal?: boolean): UnparsedSource;
|
||||
function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
tests/cases/compiler/a.js(7,7): error TS2375: Type '{ value: undefined; }' is not assignable to type 'A' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
|
||||
Types of property 'value' are incompatible.
|
||||
Type 'undefined' is not assignable to type 'number'.
|
||||
tests/cases/compiler/a.js(14,7): error TS2375: Type '{ value: undefined; }' is not assignable to type 'B' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
|
||||
Types of property 'value' are incompatible.
|
||||
Type 'undefined' is not assignable to type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/a.js (2 errors) ====
|
||||
/**
|
||||
* @typedef {object} A
|
||||
* @property {number} [value]
|
||||
*/
|
||||
|
||||
/** @type {A} */
|
||||
const a = { value: undefined }; // error
|
||||
~
|
||||
!!! error TS2375: Type '{ value: undefined; }' is not assignable to type 'A' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
|
||||
!!! error TS2375: Types of property 'value' are incompatible.
|
||||
!!! error TS2375: Type 'undefined' is not assignable to type 'number'.
|
||||
|
||||
/**
|
||||
* @typedef {{ value?: number }} B
|
||||
*/
|
||||
|
||||
/** @type {B} */
|
||||
const b = { value: undefined }; // error
|
||||
~
|
||||
!!! error TS2375: Type '{ value: undefined; }' is not assignable to type 'B' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
|
||||
!!! error TS2375: Types of property 'value' are incompatible.
|
||||
!!! error TS2375: Type 'undefined' is not assignable to type 'number'.
|
||||
|
||||
22
tests/baselines/reference/strictOptionalProperties3.symbols
Normal file
22
tests/baselines/reference/strictOptionalProperties3.symbols
Normal file
@ -0,0 +1,22 @@
|
||||
=== tests/cases/compiler/a.js ===
|
||||
/**
|
||||
* @typedef {object} A
|
||||
* @property {number} [value]
|
||||
*/
|
||||
|
||||
/** @type {A} */
|
||||
const a = { value: undefined }; // error
|
||||
>a : Symbol(a, Decl(a.js, 6, 5))
|
||||
>value : Symbol(value, Decl(a.js, 6, 11))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
/**
|
||||
* @typedef {{ value?: number }} B
|
||||
*/
|
||||
|
||||
/** @type {B} */
|
||||
const b = { value: undefined }; // error
|
||||
>b : Symbol(b, Decl(a.js, 13, 5))
|
||||
>value : Symbol(value, Decl(a.js, 13, 11))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
24
tests/baselines/reference/strictOptionalProperties3.types
Normal file
24
tests/baselines/reference/strictOptionalProperties3.types
Normal file
@ -0,0 +1,24 @@
|
||||
=== tests/cases/compiler/a.js ===
|
||||
/**
|
||||
* @typedef {object} A
|
||||
* @property {number} [value]
|
||||
*/
|
||||
|
||||
/** @type {A} */
|
||||
const a = { value: undefined }; // error
|
||||
>a : A
|
||||
>{ value: undefined } : { value: undefined; }
|
||||
>value : undefined
|
||||
>undefined : undefined
|
||||
|
||||
/**
|
||||
* @typedef {{ value?: number }} B
|
||||
*/
|
||||
|
||||
/** @type {B} */
|
||||
const b = { value: undefined }; // error
|
||||
>b : B
|
||||
>{ value: undefined } : { value: undefined; }
|
||||
>value : undefined
|
||||
>undefined : undefined
|
||||
|
||||
22
tests/baselines/reference/strictOptionalProperties4.symbols
Normal file
22
tests/baselines/reference/strictOptionalProperties4.symbols
Normal file
@ -0,0 +1,22 @@
|
||||
=== tests/cases/compiler/a.js ===
|
||||
/**
|
||||
* @typedef Foo
|
||||
* @property {number} [foo]
|
||||
*/
|
||||
|
||||
const x = /** @type {Foo} */ ({});
|
||||
>x : Symbol(x, Decl(a.js, 5, 5))
|
||||
|
||||
x.foo; // number | undefined
|
||||
>x.foo : Symbol(foo, Decl(a.js, 2, 3))
|
||||
>x : Symbol(x, Decl(a.js, 5, 5))
|
||||
>foo : Symbol(foo, Decl(a.js, 2, 3))
|
||||
|
||||
const y = /** @type {Required<Foo>} */ ({});
|
||||
>y : Symbol(y, Decl(a.js, 8, 5))
|
||||
|
||||
y.foo; // number
|
||||
>y.foo : Symbol(foo, Decl(a.js, 2, 3))
|
||||
>y : Symbol(y, Decl(a.js, 8, 5))
|
||||
>foo : Symbol(foo, Decl(a.js, 2, 3))
|
||||
|
||||
26
tests/baselines/reference/strictOptionalProperties4.types
Normal file
26
tests/baselines/reference/strictOptionalProperties4.types
Normal file
@ -0,0 +1,26 @@
|
||||
=== tests/cases/compiler/a.js ===
|
||||
/**
|
||||
* @typedef Foo
|
||||
* @property {number} [foo]
|
||||
*/
|
||||
|
||||
const x = /** @type {Foo} */ ({});
|
||||
>x : Foo
|
||||
>({}) : Foo
|
||||
>{} : {}
|
||||
|
||||
x.foo; // number | undefined
|
||||
>x.foo : number | undefined
|
||||
>x : Foo
|
||||
>foo : number | undefined
|
||||
|
||||
const y = /** @type {Required<Foo>} */ ({});
|
||||
>y : Required<Foo>
|
||||
>({}) : Required<Foo>
|
||||
>{} : {}
|
||||
|
||||
y.foo; // number
|
||||
>y.foo : number
|
||||
>y : Required<Foo>
|
||||
>foo : number
|
||||
|
||||
21
tests/cases/compiler/strictOptionalProperties3.ts
Normal file
21
tests/cases/compiler/strictOptionalProperties3.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// @strictNullChecks: true
|
||||
// @exactOptionalPropertyTypes: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @filename: a.js
|
||||
|
||||
/**
|
||||
* @typedef {object} A
|
||||
* @property {number} [value]
|
||||
*/
|
||||
|
||||
/** @type {A} */
|
||||
const a = { value: undefined }; // error
|
||||
|
||||
/**
|
||||
* @typedef {{ value?: number }} B
|
||||
*/
|
||||
|
||||
/** @type {B} */
|
||||
const b = { value: undefined }; // error
|
||||
17
tests/cases/compiler/strictOptionalProperties4.ts
Normal file
17
tests/cases/compiler/strictOptionalProperties4.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// @strictNullChecks: true
|
||||
// @exactOptionalPropertyTypes: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @filename: a.js
|
||||
|
||||
/**
|
||||
* @typedef Foo
|
||||
* @property {number} [foo]
|
||||
*/
|
||||
|
||||
const x = /** @type {Foo} */ ({});
|
||||
x.foo; // number | undefined
|
||||
|
||||
const y = /** @type {Required<Foo>} */ ({});
|
||||
y.foo; // number
|
||||
Loading…
x
Reference in New Issue
Block a user