Skip to content

Commit

Permalink
Fix flow control in the presence of simplifiable types
Browse files Browse the repository at this point in the history
  • Loading branch information
weswigham committed Aug 8, 2018
1 parent 518046c commit 17d37ec
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 9 deletions.
18 changes: 9 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14028,10 +14028,10 @@ namespace ts {
getInitialTypeOfBindingElement(node);
}

function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression, reference: Node) {
return getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
getInitialType(<VariableDeclaration | BindingElement>node) :
getAssignedType(node);
getAssignedType(node), reference);
}

function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
Expand Down Expand Up @@ -14414,11 +14414,11 @@ namespace ts {
if (isEmptyArrayAssignment(node)) {
return getEvolvingArrayType(neverType);
}
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node));
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node, reference));
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
}
if (declaredType.flags & TypeFlags.Union) {
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node, reference));
}
return declaredType;
}
Expand Down Expand Up @@ -15030,14 +15030,14 @@ namespace ts {
return type.flags & TypeFlags.InstantiableNonPrimitive && maybeTypeOfKind(getBaseConstraintOfType(type) || emptyObjectType, TypeFlags.Nullable);
}

function getConstraintForLocation(type: Type, node: Node): Type;
function getConstraintForLocation(type: Type | undefined, node: Node): Type | undefined;
function getConstraintForLocation(type: Type, node: Node): Type | undefined {
function getConstraintForLocation(type: Type, node?: Node): Type;
function getConstraintForLocation(type: Type | undefined, node?: Node): Type | undefined;
function getConstraintForLocation(type: Type, node?: Node): Type | undefined {
// When a node is the left hand expression of a property access, element access, or call expression,
// and the type of the node includes type variables with constraints that are nullable, we fetch the
// apparent type of the node *before* performing control flow analysis such that narrowings apply to
// the constraint type.
if (type && isConstraintPosition(node) && forEachType(type, typeHasNullableConstraint)) {
if (node && type && isConstraintPosition(node) && forEachType(type, typeHasNullableConstraint)) {
return mapType(getWidenedType(type), getBaseConstraintOrType);
}
return type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//// [thisIndexOnExistingReadonlyFieldIsNotNever.ts]
declare class Component<P, S = {}> {
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
state: Readonly<S>;
}
interface CoachMarkAnchorProps<C> {
anchorRef?: (anchor: C) => void;
}
type AnchorType<P> = Component<P>;

class CoachMarkAnchorDecorator {
decorateComponent<P>(anchor: P) {
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
private _onAnchorRef = (anchor: AnchorType<P>) => {
const anchorRef = this.props.anchorRef;
if (anchorRef) {
anchorRef(anchor);
}
}
};
}
}


//// [thisIndexOnExistingReadonlyFieldIsNotNever.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var CoachMarkAnchorDecorator = /** @class */ (function () {
function CoachMarkAnchorDecorator() {
}
CoachMarkAnchorDecorator.prototype.decorateComponent = function (anchor) {
return /** @class */ (function (_super) {
__extends(CoachMarkAnchor, _super);
function CoachMarkAnchor() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._onAnchorRef = function (anchor) {
var anchorRef = _this.props.anchorRef;
if (anchorRef) {
anchorRef(anchor);
}
};
return _this;
}
return CoachMarkAnchor;
}(Component));
};
return CoachMarkAnchorDecorator;
}());
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
=== tests/cases/compiler/thisIndexOnExistingReadonlyFieldIsNotNever.ts ===
declare class Component<P, S = {}> {
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 24))
>S : Symbol(S, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 26))

readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
>props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>children : Symbol(children, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 1, 30))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 24))

state: Readonly<S>;
>state : Symbol(Component.state, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 1, 67))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>S : Symbol(S, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 26))
}
interface CoachMarkAnchorProps<C> {
>CoachMarkAnchorProps : Symbol(CoachMarkAnchorProps, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 3, 1))
>C : Symbol(C, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 31))

anchorRef?: (anchor: C) => void;
>anchorRef : Symbol(CoachMarkAnchorProps.anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 5, 17))
>C : Symbol(C, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 31))
}
type AnchorType<P> = Component<P>;
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 16))
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 16))

class CoachMarkAnchorDecorator {
>CoachMarkAnchorDecorator : Symbol(CoachMarkAnchorDecorator, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 7, 34))

decorateComponent<P>(anchor: P) {
>decorateComponent : Symbol(CoachMarkAnchorDecorator.decorateComponent, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 9, 32))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 25))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))

return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
>CoachMarkAnchor : Symbol(CoachMarkAnchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 14))
>Component : Symbol(Component, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 0))
>CoachMarkAnchorProps : Symbol(CoachMarkAnchorProps, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 3, 1))
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))

private _onAnchorRef = (anchor: AnchorType<P>) => {
>_onAnchorRef : Symbol(CoachMarkAnchor._onAnchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 101))
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 12, 36))
>AnchorType : Symbol(AnchorType, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 6, 1))
>P : Symbol(P, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 10, 22))

const anchorRef = this.props.anchorRef;
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))
>this.props.anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))
>this.props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
>this : Symbol(CoachMarkAnchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 11, 14))
>props : Symbol(Component.props, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 0, 36))
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 4, 35))

if (anchorRef) {
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))

anchorRef(anchor);
>anchorRef : Symbol(anchorRef, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 13, 21))
>anchor : Symbol(anchor, Decl(thisIndexOnExistingReadonlyFieldIsNotNever.ts, 12, 36))
}
}
};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
=== tests/cases/compiler/thisIndexOnExistingReadonlyFieldIsNotNever.ts ===
declare class Component<P, S = {}> {
>Component : Component<P, S>

readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
>props : Readonly<{ children?: unknown; }> & Readonly<P>
>children : unknown

state: Readonly<S>;
>state : Readonly<S>
}
interface CoachMarkAnchorProps<C> {
anchorRef?: (anchor: C) => void;
>anchorRef : ((anchor: C) => void) | undefined
>anchor : C
}
type AnchorType<P> = Component<P>;
>AnchorType : Component<P, {}>

class CoachMarkAnchorDecorator {
>CoachMarkAnchorDecorator : CoachMarkAnchorDecorator

decorateComponent<P>(anchor: P) {
>decorateComponent : <P>(anchor: P) => typeof CoachMarkAnchor
>anchor : P

return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
>class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> { private _onAnchorRef = (anchor: AnchorType<P>) => { const anchorRef = this.props.anchorRef; if (anchorRef) { anchorRef(anchor); } } } : typeof CoachMarkAnchor
>CoachMarkAnchor : typeof CoachMarkAnchor
>Component : Component<CoachMarkAnchorProps<Component<P, {}>> & P, {}>

private _onAnchorRef = (anchor: AnchorType<P>) => {
>_onAnchorRef : (anchor: Component<P, {}>) => void
>(anchor: AnchorType<P>) => { const anchorRef = this.props.anchorRef; if (anchorRef) { anchorRef(anchor); } } : (anchor: Component<P, {}>) => void
>anchor : Component<P, {}>

const anchorRef = this.props.anchorRef;
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
>this.props.anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined
>this.props : Readonly<{ children?: unknown; }> & Readonly<CoachMarkAnchorProps<Component<P, {}>> & P>
>this : this
>props : Readonly<{ children?: unknown; }> & Readonly<CoachMarkAnchorProps<Component<P, {}>> & P>
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined

if (anchorRef) {
>anchorRef : (CoachMarkAnchorProps<Component<P, {}>> & P)["anchorRef"] | undefined

anchorRef(anchor);
>anchorRef(anchor) : void
>anchorRef : (anchor: Component<P, {}>) => void
>anchor : Component<P, {}>
}
}
};
}
}

22 changes: 22 additions & 0 deletions tests/cases/compiler/thisIndexOnExistingReadonlyFieldIsNotNever.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @strict: true
declare class Component<P, S = {}> {
readonly props: Readonly<{ children?: unknown }> & Readonly<P>;
state: Readonly<S>;
}
interface CoachMarkAnchorProps<C> {
anchorRef?: (anchor: C) => void;
}
type AnchorType<P> = Component<P>;

class CoachMarkAnchorDecorator {
decorateComponent<P>(anchor: P) {
return class CoachMarkAnchor extends Component<CoachMarkAnchorProps<AnchorType<P>> & P, {}> {
private _onAnchorRef = (anchor: AnchorType<P>) => {
const anchorRef = this.props.anchorRef;
if (anchorRef) {
anchorRef(anchor);
}
}
};
}
}

0 comments on commit 17d37ec

Please sign in to comment.