mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Ensure subtype relation ordering for readonly properties (#47069)
* Ensure subtype relation ordering for readonly properties * Probably fix post-LKG assignability error
This commit is contained in:
@@ -19732,6 +19732,18 @@ namespace ts {
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
// Ensure {readonly a: whatever} is not a subtype of {a: whatever},
|
||||
// while {a: whatever} is a subtype of {readonly a: whatever}.
|
||||
// This ensures the subtype relationship is ordered, and preventing declaration order
|
||||
// from deciding which type "wins" in union subtype reduction.
|
||||
// They're still assignable to one another, since `readonly` doesn't affect assignability.
|
||||
if (
|
||||
(relation === subtypeRelation || relation === strictSubtypeRelation) &&
|
||||
!!(sourcePropFlags & ModifierFlags.Readonly) && !(targetPropFlags & ModifierFlags.Readonly)
|
||||
) {
|
||||
return Ternary.False;
|
||||
}
|
||||
// If the target comes from a partial union prop, allow `undefined` in the target type
|
||||
const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState);
|
||||
if (!related) {
|
||||
|
||||
@@ -303,7 +303,7 @@ namespace ts.textChanges {
|
||||
export class ChangeTracker {
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: readonly (Statement | SyntaxKind.NewLineTrivia)[] }[] = [];
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassDeclaration | InterfaceDeclaration | ObjectLiteralExpression, readonly sourceFile: SourceFile }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, readonly sourceFile: SourceFile }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
|
||||
|
||||
public static fromContext(context: TextChangesContext): ChangeTracker {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
tests/cases/compiler/four.ts(11,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/four.ts(15,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/one.ts(11,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/one.ts(15,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/three.ts(11,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/three.ts(15,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/two.ts(11,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
tests/cases/compiler/two.ts(15,11): error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
|
||||
==== tests/cases/compiler/one.ts (2 errors) ====
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
return three;
|
||||
}
|
||||
==== tests/cases/compiler/two.ts (2 errors) ====
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
==== tests/cases/compiler/three.ts (2 errors) ====
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
==== tests/cases/compiler/four.ts (2 errors) ====
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
~
|
||||
!!! error TS2540: Cannot assign to 'a' because it is a read-only property.
|
||||
|
||||
return three;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//// [tests/cases/compiler/readonlyPropertySubtypeRelationDirected.ts] ////
|
||||
|
||||
//// [one.ts]
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
//// [two.ts]
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
//// [three.ts]
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
//// [four.ts]
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
//// [one.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
var two = { a: 'two' };
|
||||
var one = { a: 'one' };
|
||||
function doSomething(condition) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
var three = (condition) ? one : two;
|
||||
three.a = 'foo';
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
return three;
|
||||
}
|
||||
//// [two.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
var two = { a: 'two' };
|
||||
var one = { a: 'one' };
|
||||
function doSomething(condition) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
var three = (condition) ? two : one;
|
||||
three.a = 'foo';
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
return three;
|
||||
}
|
||||
//// [three.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
var one = { a: 'one' };
|
||||
var two = { a: 'two' };
|
||||
function doSomething(condition) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
var three = (condition) ? one : two;
|
||||
three.a = 'foo';
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
return three;
|
||||
}
|
||||
//// [four.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
var one = { a: 'one' };
|
||||
var two = { a: 'two' };
|
||||
function doSomething(condition) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
var three = (condition) ? two : one;
|
||||
three.a = 'foo';
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
return three;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
=== tests/cases/compiler/one.ts ===
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : Symbol(two, Decl(one.ts, 2, 5))
|
||||
>a : Symbol(a, Decl(one.ts, 2, 12))
|
||||
>a : Symbol(a, Decl(one.ts, 2, 28))
|
||||
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : Symbol(one, Decl(one.ts, 3, 5))
|
||||
>a : Symbol(a, Decl(one.ts, 3, 12))
|
||||
>a : Symbol(a, Decl(one.ts, 3, 37))
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : Symbol(doSomething, Decl(one.ts, 3, 49))
|
||||
>condition : Symbol(condition, Decl(one.ts, 5, 21))
|
||||
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
>three : Symbol(three, Decl(one.ts, 8, 9))
|
||||
>condition : Symbol(condition, Decl(one.ts, 5, 21))
|
||||
>one : Symbol(one, Decl(one.ts, 3, 5))
|
||||
>two : Symbol(two, Decl(one.ts, 2, 5))
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a : Symbol(a, Decl(one.ts, 3, 12))
|
||||
>three : Symbol(three, Decl(one.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(one.ts, 3, 12))
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a : Symbol(a, Decl(one.ts, 3, 12))
|
||||
>three : Symbol(three, Decl(one.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(one.ts, 3, 12))
|
||||
|
||||
return three;
|
||||
>three : Symbol(three, Decl(one.ts, 8, 9))
|
||||
}
|
||||
=== tests/cases/compiler/two.ts ===
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : Symbol(two, Decl(two.ts, 2, 5))
|
||||
>a : Symbol(a, Decl(two.ts, 2, 12))
|
||||
>a : Symbol(a, Decl(two.ts, 2, 28))
|
||||
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : Symbol(one, Decl(two.ts, 3, 5))
|
||||
>a : Symbol(a, Decl(two.ts, 3, 12))
|
||||
>a : Symbol(a, Decl(two.ts, 3, 37))
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : Symbol(doSomething, Decl(two.ts, 3, 49))
|
||||
>condition : Symbol(condition, Decl(two.ts, 5, 21))
|
||||
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
>three : Symbol(three, Decl(two.ts, 8, 9))
|
||||
>condition : Symbol(condition, Decl(two.ts, 5, 21))
|
||||
>two : Symbol(two, Decl(two.ts, 2, 5))
|
||||
>one : Symbol(one, Decl(two.ts, 3, 5))
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a : Symbol(a, Decl(two.ts, 3, 12))
|
||||
>three : Symbol(three, Decl(two.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(two.ts, 3, 12))
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a : Symbol(a, Decl(two.ts, 3, 12))
|
||||
>three : Symbol(three, Decl(two.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(two.ts, 3, 12))
|
||||
|
||||
return three;
|
||||
>three : Symbol(three, Decl(two.ts, 8, 9))
|
||||
}
|
||||
|
||||
=== tests/cases/compiler/three.ts ===
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : Symbol(one, Decl(three.ts, 2, 5))
|
||||
>a : Symbol(a, Decl(three.ts, 2, 12))
|
||||
>a : Symbol(a, Decl(three.ts, 2, 37))
|
||||
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : Symbol(two, Decl(three.ts, 3, 5))
|
||||
>a : Symbol(a, Decl(three.ts, 3, 12))
|
||||
>a : Symbol(a, Decl(three.ts, 3, 28))
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : Symbol(doSomething, Decl(three.ts, 3, 40))
|
||||
>condition : Symbol(condition, Decl(three.ts, 5, 21))
|
||||
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
>three : Symbol(three, Decl(three.ts, 8, 9))
|
||||
>condition : Symbol(condition, Decl(three.ts, 5, 21))
|
||||
>one : Symbol(one, Decl(three.ts, 2, 5))
|
||||
>two : Symbol(two, Decl(three.ts, 3, 5))
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a : Symbol(a, Decl(three.ts, 2, 12))
|
||||
>three : Symbol(three, Decl(three.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(three.ts, 2, 12))
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a : Symbol(a, Decl(three.ts, 2, 12))
|
||||
>three : Symbol(three, Decl(three.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(three.ts, 2, 12))
|
||||
|
||||
return three;
|
||||
>three : Symbol(three, Decl(three.ts, 8, 9))
|
||||
}
|
||||
|
||||
=== tests/cases/compiler/four.ts ===
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : Symbol(one, Decl(four.ts, 2, 5))
|
||||
>a : Symbol(a, Decl(four.ts, 2, 12))
|
||||
>a : Symbol(a, Decl(four.ts, 2, 37))
|
||||
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : Symbol(two, Decl(four.ts, 3, 5))
|
||||
>a : Symbol(a, Decl(four.ts, 3, 12))
|
||||
>a : Symbol(a, Decl(four.ts, 3, 28))
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : Symbol(doSomething, Decl(four.ts, 3, 40))
|
||||
>condition : Symbol(condition, Decl(four.ts, 5, 21))
|
||||
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
>three : Symbol(three, Decl(four.ts, 8, 9))
|
||||
>condition : Symbol(condition, Decl(four.ts, 5, 21))
|
||||
>two : Symbol(two, Decl(four.ts, 3, 5))
|
||||
>one : Symbol(one, Decl(four.ts, 2, 5))
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a : Symbol(a, Decl(four.ts, 2, 12))
|
||||
>three : Symbol(three, Decl(four.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(four.ts, 2, 12))
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a : Symbol(a, Decl(four.ts, 2, 12))
|
||||
>three : Symbol(three, Decl(four.ts, 8, 9))
|
||||
>a : Symbol(a, Decl(four.ts, 2, 12))
|
||||
|
||||
return three;
|
||||
>three : Symbol(three, Decl(four.ts, 8, 9))
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
=== tests/cases/compiler/one.ts ===
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : { a: string; }
|
||||
>a : string
|
||||
>{ a: 'two' } : { a: string; }
|
||||
>a : string
|
||||
>'two' : "two"
|
||||
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : { readonly a: string; }
|
||||
>a : string
|
||||
>{ a: 'one' } : { a: string; }
|
||||
>a : string
|
||||
>'one' : "one"
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : (condition: boolean) => { readonly a: string; }
|
||||
>condition : boolean
|
||||
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
>three : { readonly a: string; }
|
||||
>(condition) ? one : two : { readonly a: string; }
|
||||
>(condition) : boolean
|
||||
>condition : boolean
|
||||
>one : { readonly a: string; }
|
||||
>two : { a: string; }
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a = 'foo' : "foo"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo' : "foo"
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a = 'foo2' : "foo2"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo2' : "foo2"
|
||||
|
||||
return three;
|
||||
>three : { readonly a: string; }
|
||||
}
|
||||
=== tests/cases/compiler/two.ts ===
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : { a: string; }
|
||||
>a : string
|
||||
>{ a: 'two' } : { a: string; }
|
||||
>a : string
|
||||
>'two' : "two"
|
||||
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : { readonly a: string; }
|
||||
>a : string
|
||||
>{ a: 'one' } : { a: string; }
|
||||
>a : string
|
||||
>'one' : "one"
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : (condition: boolean) => { readonly a: string; }
|
||||
>condition : boolean
|
||||
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
>three : { readonly a: string; }
|
||||
>(condition) ? two : one : { readonly a: string; }
|
||||
>(condition) : boolean
|
||||
>condition : boolean
|
||||
>two : { a: string; }
|
||||
>one : { readonly a: string; }
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a = 'foo' : "foo"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo' : "foo"
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a = 'foo2' : "foo2"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo2' : "foo2"
|
||||
|
||||
return three;
|
||||
>three : { readonly a: string; }
|
||||
}
|
||||
|
||||
=== tests/cases/compiler/three.ts ===
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : { readonly a: string; }
|
||||
>a : string
|
||||
>{ a: 'one' } : { a: string; }
|
||||
>a : string
|
||||
>'one' : "one"
|
||||
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : { a: string; }
|
||||
>a : string
|
||||
>{ a: 'two' } : { a: string; }
|
||||
>a : string
|
||||
>'two' : "two"
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : (condition: boolean) => { readonly a: string; }
|
||||
>condition : boolean
|
||||
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
>three : { readonly a: string; }
|
||||
>(condition) ? one : two : { readonly a: string; }
|
||||
>(condition) : boolean
|
||||
>condition : boolean
|
||||
>one : { readonly a: string; }
|
||||
>two : { a: string; }
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a = 'foo' : "foo"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo' : "foo"
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a = 'foo2' : "foo2"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo2' : "foo2"
|
||||
|
||||
return three;
|
||||
>three : { readonly a: string; }
|
||||
}
|
||||
|
||||
=== tests/cases/compiler/four.ts ===
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
>one : { readonly a: string; }
|
||||
>a : string
|
||||
>{ a: 'one' } : { a: string; }
|
||||
>a : string
|
||||
>'one' : "one"
|
||||
|
||||
const two: { a: string } = { a: 'two' };
|
||||
>two : { a: string; }
|
||||
>a : string
|
||||
>{ a: 'two' } : { a: string; }
|
||||
>a : string
|
||||
>'two' : "two"
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
>doSomething : (condition: boolean) => { readonly a: string; }
|
||||
>condition : boolean
|
||||
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
>three : { readonly a: string; }
|
||||
>(condition) ? two : one : { readonly a: string; }
|
||||
>(condition) : boolean
|
||||
>condition : boolean
|
||||
>two : { a: string; }
|
||||
>one : { readonly a: string; }
|
||||
|
||||
three.a = 'foo';
|
||||
>three.a = 'foo' : "foo"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo' : "foo"
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
>three.a = 'foo2' : "foo2"
|
||||
>three.a : any
|
||||
>three : { readonly a: string; }
|
||||
>a : any
|
||||
>'foo2' : "foo2"
|
||||
|
||||
return three;
|
||||
>three : { readonly a: string; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// @strict: true
|
||||
// @filename: one.ts
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
// @filename: two.ts
|
||||
export {};
|
||||
// When the non-readonly type is declared first, the unioned type of `three` in `doSomething` is never treated as readonly
|
||||
const two: { a: string } = { a: 'two' };
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
// @filename: three.ts
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `one` comes first in the conditional check, the return type of `doSomething` is inferred as `a` is readonly, but `a` is
|
||||
// only treated as readonly (i.e. it will produce a diagnostic if you try to assign to it) based on the order of declarations of `one` and `two` above
|
||||
const three = (condition) ? one : two;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
|
||||
// @filename: four.ts
|
||||
export {};
|
||||
// When the readonly type is declared first, the unioned type of `three` in `doSomething` is always treated as readonly by the compiler
|
||||
const one: { readonly a: string } = { a: 'one' };
|
||||
const two: { a: string } = { a: 'two' };
|
||||
|
||||
function doSomething(condition: boolean) {
|
||||
// when `two` comes first in the conditional check, the return type of `doSomething` is inferred as not readonly but produces the same diagnostics as above
|
||||
// based on the declaration order of `one` and `two`
|
||||
const three = (condition) ? two : one;
|
||||
|
||||
three.a = 'foo';
|
||||
|
||||
// the inferred (displayed?) type of `a` also depends on the order of the condition above. When `one` comes first, the displayed type is `any`
|
||||
// when `two` comes first, the displayed type is `string`, but the diagnostic will always correctly find that it's string
|
||||
three.a = 'foo2';
|
||||
|
||||
return three;
|
||||
}
|
||||
Reference in New Issue
Block a user