Skip to content

Commit 17d37ec

Browse files
committed
Fix flow control in the presence of simplifiable types
1 parent 518046c commit 17d37ec

5 files changed

+224
-9
lines changed

src/compiler/checker.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -14028,10 +14028,10 @@ namespace ts {
1402814028
getInitialTypeOfBindingElement(node);
1402914029
}
1403014030

14031-
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
14032-
return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
14031+
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression, reference: Node) {
14032+
return getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
1403314033
getInitialType(<VariableDeclaration | BindingElement>node) :
14034-
getAssignedType(node);
14034+
getAssignedType(node), reference);
1403514035
}
1403614036

1403714037
function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
@@ -14414,11 +14414,11 @@ namespace ts {
1441414414
if (isEmptyArrayAssignment(node)) {
1441514415
return getEvolvingArrayType(neverType);
1441614416
}
14417-
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node));
14417+
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node, reference));
1441814418
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
1441914419
}
1442014420
if (declaredType.flags & TypeFlags.Union) {
14421-
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
14421+
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node, reference));
1442214422
}
1442314423
return declaredType;
1442414424
}
@@ -15030,14 +15030,14 @@ namespace ts {
1503015030
return type.flags & TypeFlags.InstantiableNonPrimitive && maybeTypeOfKind(getBaseConstraintOfType(type) || emptyObjectType, TypeFlags.Nullable);
1503115031
}
1503215032

15033-
function getConstraintForLocation(type: Type, node: Node): Type;
15034-
function getConstraintForLocation(type: Type | undefined, node: Node): Type | undefined;
15035-
function getConstraintForLocation(type: Type, node: Node): Type | undefined {
15033+
function getConstraintForLocation(type: Type, node?: Node): Type;
15034+
function getConstraintForLocation(type: Type | undefined, node?: Node): Type | undefined;
15035+
function getConstraintForLocation(type: Type, node?: Node): Type | undefined {
1503615036
// When a node is the left hand expression of a property access, element access, or call expression,
1503715037
// and the type of the node includes type variables with constraints that are nullable, we fetch the
1503815038
// apparent type of the node *before* performing control flow analysis such that narrowings apply to
1503915039
// the constraint type.
15040-
if (type && isConstraintPosition(node) && forEachType(type, typeHasNullableConstraint)) {
15040+
if (node && type && isConstraintPosition(node) && forEachType(type, typeHasNullableConstraint)) {
1504115041
return mapType(getWidenedType(type), getBaseConstraintOrType);
1504215042
}
1504315043
return type;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//// [thisIndexOnExistingReadonlyFieldIsNotNever.ts]
2+
declare class Component<P, S = {}> {
3+
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
4+
state: Readonly<S>;
5+
}
6+
interface CoachMarkAnchorProps<C> {
7+
anchorRef?: (anchor: C) => void;
8+
}
9+
type AnchorType<P> = Component<P>;
10+
11+
class CoachMarkAnchorDecorator {
12+
decorateComponent<P>(anchor: P) {
13+
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
14+
private _onAnchorRef = (anchor: AnchorType<P>) => {
15+
const anchorRef = this.props.anchorRef;
16+
if (anchorRef) {
17+
anchorRef(anchor);
18+
}
19+
}
20+
};
21+
}
22+
}
23+
24+
25+
//// [thisIndexOnExistingReadonlyFieldIsNotNever.js]
26+
"use strict";
27+
var __extends = (this && this.__extends) || (function () {
28+
var extendStatics = function (d, b) {
29+
extendStatics = Object.setPrototypeOf ||
30+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
31+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
32+
return extendStatics(d, b);
33+
}
34+
return function (d, b) {
35+
extendStatics(d, b);
36+
function __() { this.constructor = d; }
37+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
38+
};
39+
})();
40+
var CoachMarkAnchorDecorator = /** @class */ (function () {
41+
function CoachMarkAnchorDecorator() {
42+
}
43+
CoachMarkAnchorDecorator.prototype.decorateComponent = function (anchor) {
44+
return /** @class */ (function (_super) {
45+
__extends(CoachMarkAnchor, _super);
46+
function CoachMarkAnchor() {
47+
var _this = _super !== null && _super.apply(this, arguments) || this;
48+
_this._onAnchorRef = function (anchor) {
49+
var anchorRef = _this.props.anchorRef;
50+
if (anchorRef) {
51+
anchorRef(anchor);
52+
}
53+
};
54+
return _this;
55+
}
56+
return CoachMarkAnchor;
57+
}(Component));
58+
};
59+
return CoachMarkAnchorDecorator;
60+
}());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
=== tests/cases/compiler/thisIndexOnExistingReadonlyFieldIsNotNever.ts ===
2+
declare class Component<P, S = {}> {
3+
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
4+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 24))
5+
>S : Symbol(S, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 26))
6+
7+
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
8+
>props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
9+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
10+
>children : Symbol(children, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 1, 30))
11+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
12+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 24))
13+
14+
state: Readonly<S>;
15+
>state : Symbol(Component.state, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 1, 67))
16+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
17+
>S : Symbol(S, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 26))
18+
}
19+
interface CoachMarkAnchorProps<C> {
20+
>CoachMarkAnchorProps : Symbol(CoachMarkAnchorProps, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 3, 1))
21+
>C : Symbol(C, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 31))
22+
23+
anchorRef?: (anchor: C) => void;
24+
>anchorRef : Symbol(CoachMarkAnchorProps.anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))
25+
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 5, 17))
26+
>C : Symbol(C, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 31))
27+
}
28+
type AnchorType<P> = Component<P>;
29+
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
30+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 16))
31+
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
32+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 16))
33+
34+
class CoachMarkAnchorDecorator {
35+
>CoachMarkAnchorDecorator : Symbol(CoachMarkAnchorDecorator, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 34))
36+
37+
decorateComponent<P>(anchor: P) {
38+
>decorateComponent : Symbol(CoachMarkAnchorDecorator.decorateComponent, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 9, 32))
39+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
40+
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 25))
41+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
42+
43+
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
44+
>CoachMarkAnchor : Symbol(CoachMarkAnchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 14))
45+
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
46+
>CoachMarkAnchorProps : Symbol(CoachMarkAnchorProps, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 3, 1))
47+
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
48+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
49+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
50+
51+
private _onAnchorRef = (anchor: AnchorType<P>) => {
52+
>_onAnchorRef : Symbol(CoachMarkAnchor._onAnchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 101))
53+
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 12, 36))
54+
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
55+
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
56+
57+
const anchorRef = this.props.anchorRef;
58+
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))
59+
>this.props.anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))
60+
>this.props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
61+
>this : Symbol(CoachMarkAnchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 14))
62+
>props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
63+
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))
64+
65+
if (anchorRef) {
66+
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))
67+
68+
anchorRef(anchor);
69+
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))
70+
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 12, 36))
71+
}
72+
}
73+
};
74+
}
75+
}
76+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/thisIndexOnExistingReadonlyFieldIsNotNever.ts ===
2+
declare class Component<P, S = {}> {
3+
>Component : Component<P, S>
4+
5+
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
6+
>props : Readonly<{ children?: unknown; }> & Readonly<P>
7+
>children : unknown
8+
9+
state: Readonly<S>;
10+
>state : Readonly<S>
11+
}
12+
interface CoachMarkAnchorProps<C> {
13+
anchorRef?: (anchor: C) => void;
14+
>anchorRef : ((anchor: C) => void) | undefined
15+
>anchor : C
16+
}
17+
type AnchorType<P> = Component<P>;
18+
>AnchorType : Component<P, {}>
19+
20+
class CoachMarkAnchorDecorator {
21+
>CoachMarkAnchorDecorator : CoachMarkAnchorDecorator
22+
23+
decorateComponent<P>(anchor: P) {
24+
>decorateComponent : <P>(anchor: P) => typeof CoachMarkAnchor
25+
>anchor : P
26+
27+
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
28+
>class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> { private _onAnchorRef = (anchor: AnchorType<P>) => { const anchorRef = this.props.anchorRef; if (anchorRef) { anchorRef(anchor); } } } : typeof CoachMarkAnchor
29+
>CoachMarkAnchor : typeof CoachMarkAnchor
30+
>Component : Component<CoachMarkAnchorProps<Component<P, {}>> & P, {}>
31+
32+
private _onAnchorRef = (anchor: AnchorType<P>) => {
33+
>_onAnchorRef : (anchor: Component<P, {}>) => void
34+
>(anchor: AnchorType<P>) => { const anchorRef = this.props.anchorRef; if (anchorRef) { anchorRef(anchor); } } : (anchor: Component<P, {}>) => void
35+
>anchor : Component<P, {}>
36+
37+
const anchorRef = this.props.anchorRef;
38+
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
39+
>this.props.anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
40+
>this.props : Readonly<{ children?: unknown; }> & Readonly<CoachMarkAnchorProps<Component<P, {}>> & P>
41+
>this : this
42+
>props : Readonly<{ children?: unknown; }> & Readonly<CoachMarkAnchorProps<Component<P, {}>> & P>
43+
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
44+
45+
if (anchorRef) {
46+
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
47+
48+
anchorRef(anchor);
49+
>anchorRef(anchor) : void
50+
>anchorRef : (anchor: Component<P, {}>) => void
51+
>anchor : Component<P, {}>
52+
}
53+
}
54+
};
55+
}
56+
}
57+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @strict: true
2+
declare class Component<P, S = {}> {
3+
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
4+
state: Readonly<S>;
5+
}
6+
interface CoachMarkAnchorProps<C> {
7+
anchorRef?: (anchor: C) => void;
8+
}
9+
type AnchorType<P> = Component<P>;
10+
11+
class CoachMarkAnchorDecorator {
12+
decorateComponent<P>(anchor: P) {
13+
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
14+
private _onAnchorRef = (anchor: AnchorType<P>) => {
15+
const anchorRef = this.props.anchorRef;
16+
if (anchorRef) {
17+
anchorRef(anchor);
18+
}
19+
}
20+
};
21+
}
22+
}

0 commit comments

Comments
 (0)