Fix unassignable properties by adding undefined with exactOptionalPropertyTypes (#45032)

* Simple first version

Doesn't cover or test any complicated variations.

* Lots of cases work

Destructuring does not. But

- skipping node_modules and lib.* does.
- call expressions does
- property access, including with private identifiers, does

* Support variable declarations, property assignments, destructuring

As long as it's not nested

* More cleanup

* skip all d.ts, not just node_modules/lib

* Offer a codefix for a lot more cases

* remove incorrect tuple check

* Use getSymbolId instead of converting to string

Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>

* add test + switch to tracking number symbol ids

* Address PR comments

* Exclude tuples from suggestion

* Better way to get error node

Plus add a check that errorNode is an argument to the call, not the
call's expression.

* fix semicolon lint

* fix another crash

* Simplify: add undefined to all optional propertie

whether or not somebody tried to assign undefined to them in the
erroneous assignment

* remove fix-all

Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>
This commit is contained in:
Nathan Shively-Sanders
2021-08-10 16:57:38 -07:00
committed by GitHub
co-authored by Andrew Branch
parent 92e7fb521f
commit 8d4fe5a0fb
31 changed files with 992 additions and 100 deletions
+49 -14
View File
@@ -635,6 +635,7 @@ namespace ts {
isTupleType,
isArrayLikeType,
isTypeInvalidDueToUnionDiscriminant,
getExactOptionalProperties,
getAllPossiblePropertiesOfTypes,
getSuggestedSymbolForNonexistentProperty,
getSuggestionForNonexistentProperty,
@@ -16768,24 +16769,29 @@ namespace ts {
let sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType);
if (!sourcePropType) continue;
const propName = getPropertyNameFromIndex(nameType, /*accessNode*/ undefined);
const targetIsOptional = !!(propName && (getPropertyOfType(target, propName) || unknownSymbol).flags & SymbolFlags.Optional);
const sourceIsOptional = !!(propName && (getPropertyOfType(source, propName) || unknownSymbol).flags & SymbolFlags.Optional);
targetPropType = removeMissingType(targetPropType, targetIsOptional);
sourcePropType = removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional);
if (!checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer);
if (elaborated) {
reportedError = true;
}
else {
reportedError = true;
if (!elaborated) {
// Issue error on the prop itself, since the prop couldn't elaborate the error
const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {};
// Use the expression type, if available
const specificSource = next ? checkExpressionForMutableLocationWithContextualType(next, sourcePropType) : sourcePropType;
const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
if (result && specificSource !== sourcePropType) {
// If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType
checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
if (exactOptionalPropertyTypes && isExactOptionalPropertyMismatch(specificSource, targetPropType)) {
const diag = createDiagnosticForNode(prop, Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, typeToString(specificSource), typeToString(targetPropType));
diagnostics.add(diag);
resultObj.errors = [diag];
}
else {
const targetIsOptional = !!(propName && (getPropertyOfType(target, propName) || unknownSymbol).flags & SymbolFlags.Optional);
const sourceIsOptional = !!(propName && (getPropertyOfType(source, propName) || unknownSymbol).flags & SymbolFlags.Optional);
targetPropType = removeMissingType(targetPropType, targetIsOptional);
sourcePropType = removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional);
const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
if (result && specificSource !== sourcePropType) {
// If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType
checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj);
}
}
if (resultObj.errors) {
const reportedDiag = resultObj.errors[resultObj.errors.length - 1];
@@ -16813,7 +16819,6 @@ namespace ts {
}
}
}
reportedError = true;
}
}
}
@@ -17679,10 +17684,18 @@ namespace ts {
else if (sourceType === targetType) {
message = Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated;
}
else if (exactOptionalPropertyTypes && getExactOptionalUnassignableProperties(source, target).length) {
message = Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties;
}
else {
message = Diagnostics.Type_0_is_not_assignable_to_type_1;
}
}
else if (message === Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1
&& exactOptionalPropertyTypes
&& getExactOptionalUnassignableProperties(source, target).length) {
message = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties;
}
reportError(message, generalizedSourceType, targetType);
}
@@ -19598,6 +19611,20 @@ namespace ts {
return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral);
}
function getExactOptionalUnassignableProperties(source: Type, target: Type) {
if (isTupleType(source) && isTupleType(target)) return emptyArray;
return getPropertiesOfType(target)
.filter(targetProp => isExactOptionalPropertyMismatch(getTypeOfPropertyOfType(source, targetProp.escapedName), getTypeOfSymbol(targetProp)));
}
function isExactOptionalPropertyMismatch(source: Type | undefined, target: Type | undefined) {
return !!source && !!target && maybeTypeOfKind(source, TypeFlags.Undefined) && !!containsMissingType(target);
}
function getExactOptionalProperties(type: Type) {
return getPropertiesOfType(type).filter(targetProp => containsMissingType(getTypeOfSymbol(targetProp)));
}
function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) {
return findMatchingDiscriminantType(source, target, isRelatedTo, /*skipPartial*/ true) ||
findMatchingTypeReferenceOrTypeAliasReference(source, target) ||
@@ -32479,8 +32506,16 @@ namespace ts {
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access,
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
let headMessage: DiagnosticMessage | undefined;
if (exactOptionalPropertyTypes && isPropertyAccessExpression(left) && maybeTypeOfKind(valueType, TypeFlags.Undefined)) {
const target = getTypeOfPropertyOfType(getTypeOfExpression(left.expression), left.name.escapedText);
if (isExactOptionalPropertyMismatch(valueType, target)) {
headMessage = Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target;
}
}
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right);
checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right, headMessage);
}
}
}
+16
View File
@@ -1738,6 +1738,10 @@
"category": "Error",
"code": 2374
},
"Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": {
"category": "Error",
"code": 2375
},
"A 'super' call must be the first statement in the constructor when a class contains initialized properties, parameter properties, or private identifiers.": {
"category": "Error",
"code": 2376
@@ -1750,6 +1754,10 @@
"category": "Error",
"code": 2378
},
"Argument of type '{0}' is not assignable to parameter of type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": {
"category": "Error",
"code": 2379
},
"The return type of a 'get' accessor must be assignable to its 'set' accessor type": {
"category": "Error",
"code": 2380
@@ -1874,6 +1882,10 @@
"category": "Error",
"code": 2410
},
"Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.": {
"category": "Error",
"code": 2412
},
"Property '{0}' of type '{1}' is not assignable to '{2}' index type '{3}'.": {
"category": "Error",
"code": 2411
@@ -7118,6 +7130,10 @@
"category": "Message",
"code": 95168
},
"Add 'undefined' to optional property type": {
"category": "Message",
"code": 95169
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
+1
View File
@@ -4308,6 +4308,7 @@ namespace ts {
* e.g. it specifies `kind: "a"` and obj has `kind: "b"`.
*/
/* @internal */ isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean;
/* @internal */ getExactOptionalProperties(type: Type): Symbol[];
/**
* For a union, will include a property if it's defined in *any* of the member types.
* So for `{ a } | { b }`, this will include both `a` and `b`.
+2 -1
View File
@@ -641,7 +641,8 @@ namespace FourSlash {
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
const error = errors[0];
this.raiseError(`Found an error: ${this.formatPosition(error.file!, error.start!)}: ${error.messageText}`);
const message = typeof error.messageText === "string" ? error.messageText : error.messageText.messageText;
this.raiseError(`Found an error: ${this.formatPosition(error.file!, error.start!)}: ${message}`);
}
});
}
+9 -19
View File
@@ -32,7 +32,7 @@ namespace ts.codefix {
errorCodes,
getCodeActions: context => {
const { sourceFile, errorCode, span, cancellationToken, program } = context;
const expression = getFixableErrorSpanExpression(sourceFile, errorCode, span, cancellationToken, program);
const expression = getAwaitErrorSpanExpression(sourceFile, errorCode, span, cancellationToken, program);
if (!expression) {
return;
}
@@ -48,7 +48,7 @@ namespace ts.codefix {
const checker = context.program.getTypeChecker();
const fixedDeclarations = new Set<number>();
return codeFixAll(context, errorCodes, (t, diagnostic) => {
const expression = getFixableErrorSpanExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
const expression = getAwaitErrorSpanExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
if (!expression) {
return;
}
@@ -59,6 +59,13 @@ namespace ts.codefix {
},
});
function getAwaitErrorSpanExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program) {
const expression = getFixableErrorSpanExpression(sourceFile, span);
return expression
&& isMissingAwaitError(sourceFile, errorCode, span, cancellationToken, program)
&& isInsideAwaitableBody(expression) ? expression : undefined;
}
function getDeclarationSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Set<number>) {
const { sourceFile, program, cancellationToken } = context;
const awaitableInitializers = findAwaitableInitializers(expression, sourceFile, cancellationToken, program, checker);
@@ -95,23 +102,6 @@ namespace ts.codefix {
some(relatedInformation, related => related.code === Diagnostics.Did_you_forget_to_use_await.code));
}
function getFixableErrorSpanExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program): Expression | undefined {
const token = getTokenAtPosition(sourceFile, span.start);
// Checker has already done work to determine that await might be possible, and has attached
// related info to the node, so start by finding the expression that exactly matches up
// with the diagnostic range.
const expression = findAncestor(token, node => {
if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
return "quit";
}
return isExpression(node) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
}) as Expression | undefined;
return expression
&& isMissingAwaitError(sourceFile, errorCode, span, cancellationToken, program)
&& isInsideAwaitableBody(expression) ? expression : undefined;
}
interface AwaitableInitializer {
expression: Expression;
declarationSymbol: Symbol;
@@ -0,0 +1,96 @@
/* @internal */
namespace ts.codefix {
const addOptionalPropertyUndefined = "addOptionalPropertyUndefined";
const errorCodes = [
Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target.code,
Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties.code,
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties.code,
];
registerCodeFix({
errorCodes,
getCodeActions(context) {
const typeChecker = context.program.getTypeChecker();
const toAdd = getPropertiesToAdd(context.sourceFile, context.span, typeChecker);
if (!toAdd.length) {
return undefined;
}
const changes = textChanges.ChangeTracker.with(context, t => addUndefinedToOptionalProperty(t, toAdd));
return [createCodeFixActionWithoutFixAll(addOptionalPropertyUndefined, changes, Diagnostics.Add_undefined_to_optional_property_type)];
},
fixIds: [addOptionalPropertyUndefined],
});
function getPropertiesToAdd(file: SourceFile, span: TextSpan, checker: TypeChecker): Symbol[] {
const sourceTarget = getSourceTarget(getFixableErrorSpanExpression(file, span), checker);
if (!sourceTarget) {
return emptyArray;
}
const { source: sourceNode, target: targetNode } = sourceTarget;
const target = shouldUseParentTypeOfProperty(sourceNode, targetNode, checker)
? checker.getTypeAtLocation(targetNode.expression)
: checker.getTypeAtLocation(targetNode);
if (target.symbol?.declarations?.some(d => getSourceFileOfNode(d).fileName.match(/\.d\.ts$/))) {
return emptyArray;
}
return checker.getExactOptionalProperties(target);
}
function shouldUseParentTypeOfProperty(sourceNode: Node, targetNode: Node, checker: TypeChecker): targetNode is PropertyAccessExpression {
return isPropertyAccessExpression(targetNode)
&& !!checker.getExactOptionalProperties(checker.getTypeAtLocation(targetNode.expression)).length
&& checker.getTypeAtLocation(sourceNode) === checker.getUndefinedType();
}
/**
* Find the source and target of the incorrect assignment.
* The call is recursive for property assignments.
*/
function getSourceTarget(errorNode: Node | undefined, checker: TypeChecker): { source: Node, target: Node } | undefined {
if (!errorNode) {
return undefined;
}
else if (isBinaryExpression(errorNode.parent) && errorNode.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
return { source: errorNode.parent.right, target: errorNode.parent.left };
}
else if (isVariableDeclaration(errorNode.parent) && errorNode.parent.initializer) {
return { source: errorNode.parent.initializer, target: errorNode.parent.name };
}
else if (isCallExpression(errorNode.parent)) {
const n = checker.getSymbolAtLocation(errorNode.parent.expression);
if (!n?.valueDeclaration || !isFunctionLikeKind(n.valueDeclaration.kind)) return undefined;
if (!isExpression(errorNode)) return undefined;
const i = errorNode.parent.arguments.indexOf(errorNode);
if (i === -1) return undefined;
const name = (n.valueDeclaration as any as SignatureDeclaration).parameters[i].name;
if (isIdentifier(name)) return { source: errorNode, target: name };
}
else if (isPropertyAssignment(errorNode.parent) && isIdentifier(errorNode.parent.name) ||
isShorthandPropertyAssignment(errorNode.parent)) {
const parentTarget = getSourceTarget(errorNode.parent.parent, checker);
if (!parentTarget) return undefined;
const prop = checker.getPropertyOfType(checker.getTypeAtLocation(parentTarget.target), (errorNode.parent.name as Identifier).text);
const declaration = prop?.declarations?.[0];
if (!declaration) return undefined;
return {
source: isPropertyAssignment(errorNode.parent) ? errorNode.parent.initializer : errorNode.parent.name,
target: declaration
};
}
return undefined;
}
function addUndefinedToOptionalProperty(changes: textChanges.ChangeTracker, toAdd: Symbol[]) {
for (const add of toAdd) {
const d = add.valueDeclaration;
if (d && (isPropertySignature(d) || isPropertyDeclaration(d)) && d.type) {
const t = factory.createUnionTypeNode([
...d.type.kind === SyntaxKind.UnionType ? (d.type as UnionTypeNode).types : [d.type],
factory.createTypeReferenceNode("undefined")
]);
changes.replaceNode(d.getSourceFile(), d.type, t);
}
}
}
}
+1
View File
@@ -57,6 +57,7 @@
"codefixes/addMissingDeclareProperty.ts",
"codefixes/addMissingInvocationForDecorator.ts",
"codefixes/addNameToNamelessParameter.ts",
"codefixes/addOptionalPropertyUndefined.ts",
"codefixes/annotateWithTypeFromJSDoc.ts",
"codefixes/convertFunctionToEs6Class.ts",
"codefixes/convertToAsyncFunction.ts",
+16
View File
@@ -3085,6 +3085,22 @@ namespace ts {
return createTextSpanFromBounds(startPosition, endPosition === undefined ? startPosition : endPosition);
}
/* @internal */
export function getFixableErrorSpanExpression(sourceFile: SourceFile, span: TextSpan): Expression | undefined {
const token = getTokenAtPosition(sourceFile, span.start);
// Checker has already done work to determine that await might be possible, and has attached
// related info to the node, so start by finding the expression that exactly matches up
// with the diagnostic range.
const expression = findAncestor(token, node => {
if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
return "quit";
}
return isExpression(node) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
}) as Expression | undefined;
return expression;
}
/**
* If the provided value is an array, the mapping function is applied to each element; otherwise, the mapping function is applied
* to the provided value itself.
@@ -1,9 +1,9 @@
tests/cases/compiler/strictOptionalProperties1.ts(6,5): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(12,5): error TS2322: Type 'string | undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(6,5): error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
tests/cases/compiler/strictOptionalProperties1.ts(12,5): error TS2412: Type 'string | undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(20,9): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(28,9): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(53,5): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(20,9): error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
tests/cases/compiler/strictOptionalProperties1.ts(28,9): error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
tests/cases/compiler/strictOptionalProperties1.ts(53,5): error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
tests/cases/compiler/strictOptionalProperties1.ts(60,5): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(64,5): error TS2322: Type '[number, string?, string?]' is not assignable to type '[number, string?]'.
Target allows only 2 element(s) but source may have more.
@@ -11,24 +11,43 @@ tests/cases/compiler/strictOptionalProperties1.ts(73,5): error TS2322: Type '[nu
Target allows only 3 element(s) but source may have more.
tests/cases/compiler/strictOptionalProperties1.ts(74,5): error TS2322: Type '[never?, never?, true?]' is not assignable to type '[number, string?, boolean?]'.
Source provides no match for required element at position 0 in target.
tests/cases/compiler/strictOptionalProperties1.ts(75,14): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(99,34): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(105,45): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(106,55): error TS2322: Type 'undefined' is not assignable to type 'boolean'.
tests/cases/compiler/strictOptionalProperties1.ts(107,45): error TS2322: Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(107,56): error TS2322: Type 'undefined' is not assignable to type 'boolean'.
tests/cases/compiler/strictOptionalProperties1.ts(111,31): error TS2322: Type 'undefined' is not assignable to type 'number'.
tests/cases/compiler/strictOptionalProperties1.ts(75,5): error TS2322: Type '[number, undefined, true]' is not assignable to type '[number, string?, boolean?]'.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(99,7): error TS2375: Type '{ foo: undefined; bar: string; }' is not assignable to type 'InputProps' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'foo' are incompatible.
Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(105,7): error TS2322: Type '[number, undefined]' is not assignable to type '[number, string?, boolean?]'.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(106,7): error TS2322: Type '[number, string, undefined]' is not assignable to type '[number, string?, boolean?]'.
Type at position 2 in source is not compatible with type at position 2 in target.
Type 'undefined' is not assignable to type 'boolean'.
tests/cases/compiler/strictOptionalProperties1.ts(107,7): error TS2322: Type '[number, undefined, undefined]' is not assignable to type '[number, string?, boolean?]'.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'undefined' is not assignable to type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(111,7): error TS2375: Type '{ foo: undefined; }' is not assignable to type '{ foo?: number; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'foo' are incompatible.
Type 'undefined' is not assignable to type 'number'.
tests/cases/compiler/strictOptionalProperties1.ts(119,5): error TS2411: Property 'bar' of type 'string | undefined' is not assignable to 'string' index type 'string'.
tests/cases/compiler/strictOptionalProperties1.ts(192,1): error TS2322: Type '{ a: number; b: string | undefined; }' is not assignable to type '{ [x: string]: string | number; }'.
tests/cases/compiler/strictOptionalProperties1.ts(190,15): error TS2451: Cannot redeclare block-scoped variable 'e'.
tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type 'string | boolean | undefined' is not assignable to type 'string | number | undefined'.
Type 'false' is not assignable to type 'string | number | undefined'.
tests/cases/compiler/strictOptionalProperties1.ts(194,1): error TS2412: Type 'string | boolean | undefined' is not assignable to type 'string | number' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
Type 'undefined' is not assignable to type 'string | number'.
tests/cases/compiler/strictOptionalProperties1.ts(195,1): error TS2375: Type '{ name: string; email: undefined; }' is not assignable to type 'U2' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'email' are incompatible.
Type 'undefined' is not assignable to type 'string | number'.
tests/cases/compiler/strictOptionalProperties1.ts(206,13): error TS2451: Cannot redeclare block-scoped variable 'e'.
tests/cases/compiler/strictOptionalProperties1.ts(210,1): error TS2322: Type '{ a: number; b: string | undefined; }' is not assignable to type '{ [x: string]: string | number; }'.
Property 'b' is incompatible with index signature.
Type 'string | undefined' is not assignable to type 'string | number'.
Type 'undefined' is not assignable to type 'string | number'.
tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{ a: number; b?: string | undefined; }' is not assignable to type '{ [x: string]: string | number; }'.
Property 'b' is incompatible with index signature.
Type 'string | undefined' is not assignable to type 'string | number'.
tests/cases/compiler/strictOptionalProperties1.ts(211,1): error TS2322: Type 'string | boolean | undefined' is not assignable to type '{ [x: string]: string | number; }'.
Type 'undefined' is not assignable to type '{ [x: string]: string | number; }'.
==== tests/cases/compiler/strictOptionalProperties1.ts (19 errors) ====
==== tests/cases/compiler/strictOptionalProperties1.ts (23 errors) ====
function f1(obj: { a?: string, b?: string | undefined }) {
let a = obj.a; // string | undefined
let b = obj.b; // string | undefined
@@ -36,7 +55,7 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
obj.b = 'hello';
obj.a = undefined; // Error
~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
obj.b = undefined;
}
@@ -44,8 +63,8 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
obj = obj;
obj.a = obj.a; // Error
~~~~~
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'.
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! error TS2412: Type 'string | undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
!!! error TS2412: Type 'undefined' is not assignable to type 'string'.
obj.b = obj.b;
if ('a' in obj) {
obj.a;
@@ -55,7 +74,7 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
obj.a;
obj.a = obj.a; // Error
~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
}
if (obj.hasOwnProperty('a')) {
obj.a;
@@ -65,7 +84,7 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
obj.a;
obj.a = obj.a; // Error
~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
}
if ('b' in obj) {
obj.b;
@@ -92,7 +111,7 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
obj.b = 'hello';
obj.a = undefined; // Error
~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
obj.b = undefined;
}
@@ -126,8 +145,10 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
!!! error TS2322: Type '[never?, never?, true?]' is not assignable to type '[number, string?, boolean?]'.
!!! error TS2322: Source provides no match for required element at position 0 in target.
t = [42, undefined, true]; // Error
~~~~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
~
!!! error TS2322: Type '[number, undefined, true]' is not assignable to type '[number, string?, boolean?]'.
!!! error TS2322: Type at position 1 in source is not compatible with type at position 1 in target.
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
}
function f6() {
@@ -152,32 +173,38 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
const defaultProps: Pick<Props, 'foo'> = { foo: 'foo' };
const inputProps: InputProps = { foo: undefined, bar: 'bar' };
~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
!!! related TS6500 tests/cases/compiler/strictOptionalProperties1.ts:94:5: The expected type comes from property 'foo' which is declared here on type 'InputProps'
~~~~~~~~~~
!!! error TS2375: Type '{ foo: undefined; bar: string; }' is not assignable to type 'InputProps' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
!!! error TS2375: Types of property 'foo' are incompatible.
!!! error TS2375: Type 'undefined' is not assignable to type 'string'.
const completeProps: Props = { ...defaultProps, ...inputProps };
// Example from #13195
const t1: [number, string?, boolean?] = [1];
const t2: [number, string?, boolean?] = [1, undefined];
~~~~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
~~
!!! error TS2322: Type '[number, undefined]' is not assignable to type '[number, string?, boolean?]'.
!!! error TS2322: Type at position 1 in source is not compatible with type at position 1 in target.
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
const t3: [number, string?, boolean?] = [1, "string", undefined];
~~~~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'.
~~
!!! error TS2322: Type '[number, string, undefined]' is not assignable to type '[number, string?, boolean?]'.
!!! error TS2322: Type at position 2 in source is not compatible with type at position 2 in target.
!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'.
const t4: [number, string?, boolean?] = [1, undefined, undefined];
~~~~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
~~~~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'boolean'.
~~
!!! error TS2322: Type '[number, undefined, undefined]' is not assignable to type '[number, string?, boolean?]'.
!!! error TS2322: Type at position 1 in source is not compatible with type at position 1 in target.
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
// Example from #13195
const x: { foo?: number } = { foo: undefined };
~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.
!!! related TS6500 tests/cases/compiler/strictOptionalProperties1.ts:111:12: The expected type comes from property 'foo' which is declared here on type '{ foo?: number; }'
~
!!! error TS2375: Type '{ foo: undefined; }' is not assignable to type '{ foo?: number; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
!!! error TS2375: Types of property 'foo' are incompatible.
!!! error TS2375: Type 'undefined' is not assignable to type 'number'.
const y: { foo: number } = { foo: 123, ...x };
// Index signatures and strict optional properties
@@ -250,6 +277,36 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
declare function bb(input: number): void;
interface U1 {
name: string
email?: string | number | undefined
}
interface U2 {
name: string
email?: string | number
}
declare const e: string | boolean | undefined
~
!!! error TS2451: Cannot redeclare block-scoped variable 'e'.
declare const u1: U1
declare let u2: U2
u1.email = e // error, but only because boolean isn't in email's type
~~~~~~~~
!!! error TS2322: Type 'string | boolean | undefined' is not assignable to type 'string | number | undefined'.
!!! error TS2322: Type 'false' is not assignable to type 'string | number | undefined'.
u2.email = e // error, and suggest adding undefined
~~~~~~~~
!!! error TS2412: Type 'string | boolean | undefined' is not assignable to type 'string | number' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
!!! error TS2412: Type 'undefined' is not assignable to type 'string | number'.
u2 = {
~~
!!! error TS2375: Type '{ name: string; email: undefined; }' is not assignable to type 'U2' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
!!! error TS2375: Types of property 'email' are incompatible.
!!! error TS2375: Type 'undefined' is not assignable to type 'string | number'.
name: 'hi',
email: undefined
}
// Repro from #44437
declare var a: {[x: string]: number | string }
@@ -257,6 +314,8 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
declare var c: {a: number, b?: string}
declare var d: {a: number, b: string | undefined }
declare var e: {a: number, b?: string | undefined }
~
!!! error TS2451: Cannot redeclare block-scoped variable 'e'.
a = b;
a = c;
@@ -268,7 +327,6 @@ tests/cases/compiler/strictOptionalProperties1.ts(193,1): error TS2322: Type '{
!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'.
a = e; // Error
~
!!! error TS2322: Type '{ a: number; b?: string | undefined; }' is not assignable to type '{ [x: string]: string | number; }'.
!!! error TS2322: Property 'b' is incompatible with index signature.
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | number'.
!!! error TS2322: Type 'string | boolean | undefined' is not assignable to type '{ [x: string]: string | number; }'.
!!! error TS2322: Type 'undefined' is not assignable to type '{ [x: string]: string | number; }'.
@@ -180,6 +180,24 @@ function aa(input: Bar): void {
declare function bb(input: number): void;
interface U1 {
name: string
email?: string | number | undefined
}
interface U2 {
name: string
email?: string | number
}
declare const e: string | boolean | undefined
declare const u1: U1
declare let u2: U2
u1.email = e // error, but only because boolean isn't in email's type
u2.email = e // error, and suggest adding undefined
u2 = {
name: 'hi',
email: undefined
}
// Repro from #44437
declare var a: {[x: string]: number | string }
@@ -322,6 +340,12 @@ function aa(input) {
var notUndefinedVal = expectNotUndefined(input.bar);
bb(notUndefinedVal);
}
u1.email = e; // error, but only because boolean isn't in email's type
u2.email = e; // error, and suggest adding undefined
u2 = {
name: 'hi',
email: undefined
};
a = b;
a = c;
a = d; // Error
@@ -399,6 +423,17 @@ interface Bar {
}
declare function aa(input: Bar): void;
declare function bb(input: number): void;
interface U1 {
name: string;
email?: string | number | undefined;
}
interface U2 {
name: string;
email?: string | number;
}
declare const e: string | boolean | undefined;
declare const u1: U1;
declare let u2: U2;
declare var a: {
[x: string]: number | string;
};
@@ -571,45 +571,97 @@ declare function bb(input: number): void;
>bb : Symbol(bb, Decl(strictOptionalProperties1.ts, 177, 1))
>input : Symbol(input, Decl(strictOptionalProperties1.ts, 179, 20))
interface U1 {
>U1 : Symbol(U1, Decl(strictOptionalProperties1.ts, 179, 41))
name: string
>name : Symbol(U1.name, Decl(strictOptionalProperties1.ts, 181, 14))
email?: string | number | undefined
>email : Symbol(U1.email, Decl(strictOptionalProperties1.ts, 182, 16))
}
interface U2 {
>U2 : Symbol(U2, Decl(strictOptionalProperties1.ts, 184, 1))
name: string
>name : Symbol(U2.name, Decl(strictOptionalProperties1.ts, 185, 14))
email?: string | number
>email : Symbol(U2.email, Decl(strictOptionalProperties1.ts, 186, 16))
}
declare const e: string | boolean | undefined
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 189, 13))
declare const u1: U1
>u1 : Symbol(u1, Decl(strictOptionalProperties1.ts, 190, 13))
>U1 : Symbol(U1, Decl(strictOptionalProperties1.ts, 179, 41))
declare let u2: U2
>u2 : Symbol(u2, Decl(strictOptionalProperties1.ts, 191, 11))
>U2 : Symbol(U2, Decl(strictOptionalProperties1.ts, 184, 1))
u1.email = e // error, but only because boolean isn't in email's type
>u1.email : Symbol(U1.email, Decl(strictOptionalProperties1.ts, 182, 16))
>u1 : Symbol(u1, Decl(strictOptionalProperties1.ts, 190, 13))
>email : Symbol(U1.email, Decl(strictOptionalProperties1.ts, 182, 16))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 189, 13))
u2.email = e // error, and suggest adding undefined
>u2.email : Symbol(U2.email, Decl(strictOptionalProperties1.ts, 186, 16))
>u2 : Symbol(u2, Decl(strictOptionalProperties1.ts, 191, 11))
>email : Symbol(U2.email, Decl(strictOptionalProperties1.ts, 186, 16))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 189, 13))
u2 = {
>u2 : Symbol(u2, Decl(strictOptionalProperties1.ts, 191, 11))
name: 'hi',
>name : Symbol(name, Decl(strictOptionalProperties1.ts, 194, 6))
email: undefined
>email : Symbol(email, Decl(strictOptionalProperties1.ts, 195, 15))
>undefined : Symbol(undefined)
}
// Repro from #44437
declare var a: {[x: string]: number | string }
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 183, 11))
>x : Symbol(x, Decl(strictOptionalProperties1.ts, 183, 17))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>x : Symbol(x, Decl(strictOptionalProperties1.ts, 201, 17))
declare var b: {a: number, b: string}
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 184, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 184, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 184, 26))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 202, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 202, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 202, 26))
declare var c: {a: number, b?: string}
>c : Symbol(c, Decl(strictOptionalProperties1.ts, 185, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 185, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 185, 26))
>c : Symbol(c, Decl(strictOptionalProperties1.ts, 203, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 203, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 203, 26))
declare var d: {a: number, b: string | undefined }
>d : Symbol(d, Decl(strictOptionalProperties1.ts, 186, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 186, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 186, 26))
>d : Symbol(d, Decl(strictOptionalProperties1.ts, 204, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 204, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 204, 26))
declare var e: {a: number, b?: string | undefined }
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 187, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 187, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 187, 26))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 205, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 205, 16))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 205, 26))
a = b;
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 183, 11))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 184, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>b : Symbol(b, Decl(strictOptionalProperties1.ts, 202, 11))
a = c;
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 183, 11))
>c : Symbol(c, Decl(strictOptionalProperties1.ts, 185, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>c : Symbol(c, Decl(strictOptionalProperties1.ts, 203, 11))
a = d; // Error
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 183, 11))
>d : Symbol(d, Decl(strictOptionalProperties1.ts, 186, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>d : Symbol(d, Decl(strictOptionalProperties1.ts, 204, 11))
a = e; // Error
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 183, 11))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 187, 11))
>a : Symbol(a, Decl(strictOptionalProperties1.ts, 201, 11))
>e : Symbol(e, Decl(strictOptionalProperties1.ts, 189, 13))
@@ -683,6 +683,57 @@ declare function bb(input: number): void;
>bb : (input: number) => void
>input : number
interface U1 {
name: string
>name : string
email?: string | number | undefined
>email : string | number | undefined
}
interface U2 {
name: string
>name : string
email?: string | number
>email : string | number | undefined
}
declare const e: string | boolean | undefined
>e : string | boolean | undefined
declare const u1: U1
>u1 : U1
declare let u2: U2
>u2 : U2
u1.email = e // error, but only because boolean isn't in email's type
>u1.email = e : string | boolean | undefined
>u1.email : string | number | undefined
>u1 : U1
>email : string | number | undefined
>e : string | boolean | undefined
u2.email = e // error, and suggest adding undefined
>u2.email = e : string | boolean | undefined
>u2.email : string | number
>u2 : U2
>email : string | number
>e : string | boolean | undefined
u2 = {
>u2 = { name: 'hi', email: undefined} : { name: string; email: undefined; }
>u2 : U2
>{ name: 'hi', email: undefined} : { name: string; email: undefined; }
name: 'hi',
>name : string
>'hi' : "hi"
email: undefined
>email : undefined
>undefined : undefined
}
// Repro from #44437
declare var a: {[x: string]: number | string }
@@ -725,7 +776,7 @@ a = d; // Error
>d : { a: number; b: string | undefined; }
a = e; // Error
>a = e : { a: number; b?: string | undefined; }
>a = e : string | boolean | undefined
>a : { [x: string]: string | number; }
>e : { a: number; b?: string | undefined; }
>e : string | boolean | undefined
@@ -183,6 +183,24 @@ function aa(input: Bar): void {
declare function bb(input: number): void;
interface U1 {
name: string
email?: string | number | undefined
}
interface U2 {
name: string
email?: string | number
}
declare const e: string | boolean | undefined
declare const u1: U1
declare let u2: U2
u1.email = e // error, but only because boolean isn't in email's type
u2.email = e // error, and suggest adding undefined
u2 = {
name: 'hi',
email: undefined
}
// Repro from #44437
declare var a: {[x: string]: number | string }
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface I {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var i: I
//// declare var j: J
//// i/**/ = j
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface I {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var i: I
declare var j: J
i = j`,
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface IF {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// function fi(if_: IF) { return if_ }
//// fi(j/**/)
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface IF {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
function fi(if_: IF) { return if_ }
fi(j)`,
});
@@ -0,0 +1,36 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface IC {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// class C {
//// ic: IC
//// m() { this.ic/**/ = j }
//// }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface IC {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
class C {
ic: IC
m() { this.ic = j }
}`,
});
@@ -0,0 +1,38 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface IC2 {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// class C {
//// ic2: IC2
//// }
//// var c = new C()
//// c.ic2/**/ = j
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface IC2 {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
class C {
ic2: IC2
}
var c = new C()
c.ic2 = j`,
});
@@ -0,0 +1,36 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface ICP {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// class CP {
//// #icp: ICP
//// m() { this.#icp/**/ = j; console.log(this.#icp) }
//// }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface ICP {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
class CP {
#icp: ICP
m() { this.#icp = j; console.log(this.#icp) }
}`,
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface ID {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// declare var id: ID
//// ({ id/**/ } = { id: j })
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface ID {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
declare var id: ID
({ id } = { id: j })`,
});
@@ -0,0 +1,34 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface More {
//// a?: number
//// b?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// declare var more: More
//// more/**/ = j
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface More {
a?: number | undefined
b?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
declare var more: More
more = j`,
});
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface Assignment {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// var assignment/**/: Assignment = j
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface Assignment {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
var assignment: Assignment = j`,
});
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface PropertyAssignment {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// var opa/**/: { pa: PropertyAssignment } = { pa: j }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface PropertyAssignment {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
var opa: { pa: PropertyAssignment } = { pa: j }`,
});
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface ShorthandPropertyAssignment {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// var ospa/**/: { j: ShorthandPropertyAssignment } = { j }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface ShorthandPropertyAssignment {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
var ospa: { j: ShorthandPropertyAssignment } = { j }`,
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface FPA {
//// a?: number
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var j: J
//// declare function fpa(fpa: { fpa: FPA }): void
//// fpa({ fpa: j }/**/)
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface FPA {
a?: number | undefined
}
interface J {
a?: number | undefined
}
declare var j: J
declare function fpa(fpa: { fpa: FPA }): void
fpa({ fpa: j })`,
});
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties2.ts
//// import { INodeModules } from 'foo'
//// interface J {
//// a?: number | undefined
//// }
//// declare var inm: INodeModules
//// declare var j: J
//// inm/**/ = j
//// console.log(inm)
// @Filename: node_modules/@types/foo/index.d.ts
//// export interface INodeModules {
//// a?: number
//// }
verify.codeFixAvailable([]);
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties4.ts
//// interface User {
//// name: string
//// email?: string
//// }
//// const user: User = {
//// name: "Andrew",
//// email: undefined,
//// }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface User {
name: string
email?: string | undefined
}
const user: User = {
name: "Andrew",
email: undefined,
}`,
});
@@ -0,0 +1,27 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties5.ts
//// interface User {
//// name: string
//// email?: string
//// }
//// declare const user: User
//// user.email = undefined;
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`interface User {
name: string
email?: string | undefined
}
declare const user: User
user.email = undefined;`,
});
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties6.ts
// based on snapshotterInjected.ts in microsoft/playwright
//// type Data = {
//// f?: (x: number) => void,
//// additional?: number,
//// nop: string,
//// };
//// declare function e(o: any): Data;
//// e(101).f = undefined
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`type Data = {
f?: ((x: number) => void) | undefined,
additional?: number | undefined,
nop: string,
};
declare function e(o: any): Data;
e(101).f = undefined`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties6.ts
// based on snapshotterInjected.ts in microsoft/playwright
//// class Feh {
//// _requestFinished(error?: string) {
//// this._finishedPromiseCallback({ error/**/ });
//// }
//// private _finishedPromiseCallback: (arg: { error?: string }) => void = () => {};
//// }
verify.codeFixAvailable([ ]);
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
// @Filename: fixExactOptionalUnassignableProperties6.ts
// based on snapshotterInjected.ts in microsoft/playwright
//// type Data = {
//// x?: {
//// y?: number
//// }
//// }
//// declare var d: Data
//// d.x = { y: undefined }
verify.codeFixAvailable([
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
]);
verify.codeFix({
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
index: 0,
newFileContent:
`type Data = {
x?: {
y?: number | undefined
}
}
declare var d: Data
d.x = { y: undefined }`,
});
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts'/>
// @strictNullChecks: true
// @exactOptionalPropertyTypes: true
//// interface IAny {
//// a?: any
//// }
//// interface J {
//// a?: number | undefined
//// }
//// declare var iany: IAny
//// declare var j: J
//// iany/**/ = j
verify.codeFixAvailable([]);