Compare commits

...

3 commits

Author SHA1 Message Date
Daniel Rosenwasser 4f84639e47 Reimplemented Nathan's changes. 2016-08-25 15:48:12 -07:00
Daniel Rosenwasser 29c18058d6 Removed commentary from tests. 2016-08-25 15:47:52 -07:00
Daniel Rosenwasser 7e4715dfdc Stole all of Nathan's tests. 2016-08-25 12:11:06 -07:00
5 changed files with 171 additions and 10 deletions

View file

@ -3432,6 +3432,44 @@ namespace ts {
return unknownType;
}
function getTypeOfBasePropertyDeclaration(declaration: PropertyDeclaration) {
if (declaration.parent.kind === SyntaxKind.ClassDeclaration) {
const parent = <ClassLikeDeclaration>declaration.parent;
const propertyName = declaration.symbol.name;
const extendedPropertyType = getSinglePropertyTypeOfTypes(getBaseTypes(<InterfaceType>getTypeOfSymbol(getSymbolOfNode(parent))), propertyName);
if (extendedPropertyType) {
return extendedPropertyType;
}
const implementedTypeNodes = getClassImplementsHeritageClauseElements(parent);
if (implementedTypeNodes) {
return getSinglePropertyTypeOfTypes(map(implementedTypeNodes, getTypeFromTypeReference), propertyName);
}
}
return undefined;
}
function getSinglePropertyTypeOfTypes(types: Type[], propertyName: string) {
let result: Type;
for (const t of types) {
if (t !== unknownType) {
const property = getPropertyOfType(t, propertyName);
if (!property || property.valueDeclaration.flags & NodeFlags.Private) {
continue;
}
if (property.name === propertyName) {
const propertyType = getTypeOfSymbol(property);
if (result && result !== propertyType) {
// if there's more than one matching property, return undefined
return undefined;
}
result = propertyType;
}
}
}
return result;
}
function getTargetType(type: ObjectType): Type {
return type.flags & TypeFlags.Reference ? (<TypeReference>type).target : type;
}
@ -5898,7 +5936,7 @@ namespace ts {
// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElement): boolean {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isMethod(node));
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
@ -5931,8 +5969,8 @@ namespace ts {
return !node.typeParameters && areAllParametersUntyped && !isNullaryArrow;
}
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | MethodDeclaration {
return (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func);
function isContextSensitiveFunctionOrMethod(func: Node): func is FunctionExpression | MethodDeclaration {
return (isFunctionExpressionOrArrowFunction(func) || isMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func);
}
function getTypeWithoutSignatures(type: Type): Type {
@ -9374,7 +9412,7 @@ namespace ts {
}
function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol {
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
if (isContextSensitiveFunctionOrMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
return contextualSignature.thisParameter;
@ -9387,7 +9425,7 @@ namespace ts {
// Return contextual type of parameter or undefined if no contextual type is available
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
const func = parameter.parent;
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
if (isContextSensitiveFunctionOrMethod(func)) {
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife) {
const indexOfParameter = indexOf(func.parameters, parameter);
@ -9447,6 +9485,12 @@ namespace ts {
return type;
}
}
if (declaration.kind === SyntaxKind.PropertyDeclaration) {
const type = getTypeOfBasePropertyDeclaration(<PropertyDeclaration>declaration);
if (type) {
return type;
}
}
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
}
@ -9815,15 +9859,23 @@ namespace ts {
function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature {
// Only function expressions, arrow functions, and object literal methods are contextually typed.
return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)
return isFunctionExpressionOrArrowFunction(node) || isMethod(node)
? getContextualSignature(<FunctionExpression>node)
: undefined;
}
function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | MethodDeclaration) {
return isObjectLiteralMethod(node) ?
getContextualTypeForObjectLiteralMethod(node) :
getApparentTypeOfContextualType(node);
let type: Type;
if (isFunctionExpressionOrArrowFunction(node)) {
type = getApparentTypeOfContextualType(node);
}
else if (isObjectLiteralMethod(node)) {
type = getContextualTypeForObjectLiteralMethod(node);
}
else if (isMethod(node)) {
type = getTypeOfBasePropertyDeclaration(node);
}
return type;
}
// Return the contextual signature for a given expression node. A contextual type provides a
@ -9832,7 +9884,6 @@ namespace ts {
// all identical ignoring their return type, the result is same signature but with return type as
// union type of return types from these signatures
function getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
const type = getContextualTypeForFunctionLikeDeclaration(node);
if (!type) {
return undefined;

View file

@ -864,6 +864,10 @@ namespace ts {
return predicate && predicate.kind === TypePredicateKind.Identifier;
}
export function isMethod(node: Node): node is MethodDeclaration {
return node && node.kind === SyntaxKind.MethodDeclaration;
}
export function isThisTypePredicate(predicate: TypePredicate): predicate is ThisTypePredicate {
return predicate && predicate.kind === TypePredicateKind.This;
}

View file

@ -0,0 +1,11 @@
// @strictNullChecks: true
interface X {
n: number
}
class C implements X {
n = undefined;
}
class C2 implements X {
n = null;
}

View file

@ -0,0 +1,72 @@
// @noImplicitAny: true
interface Event {
time: number;
}
interface Base {
superHandle: (e: Event) => number;
}
interface Listener extends Base {
handle: (e: Event) => void;
}
interface Ringer {
ring: (times: number) => void;
}
interface StringLiteral {
literal(): "A";
literals: "A" | "B";
}
abstract class Watcher {
abstract watch(e: Event): number;
}
class Alarm extends Watcher implements Listener, Ringer, StringLiteral {
str: string;
handle = e => {
this.str = e.time;
}
superHandle = e => {
this.str = e.time;
return e.time;
}
ring(times) {
this.str = times;
}
watch(e) {
this.str = e.time;
return e.time;
}
literal() {
return "A";
}
literals = "A";
}
interface A {
p: string;
q(n: string): void;
r: string;
s: string;
}
interface B {
p: number;
q(n: number): void;
r: boolean;
s: string;
}
class C {
r: number;
}
class Multiple extends C implements A, B {
p = undefined;
q(n) {
n.length;
n.toFixed;
}
r = null;
s = null;
}
let multiple = new Multiple();
multiple.r.toFixed;
multiple.r.length;
multiple.s.length;

View file

@ -0,0 +1,23 @@
interface Long {
length: number;
}
interface Lol {
canhaz: string;
}
interface Ceiling {
location: { [coordinates: string]: [number, number] };
}
interface Invisible {
invisibles: string[];
}
class Cat implements Long, Lol, Ceiling, Invisible {
length = undefined;
canhaz = null;
location = {};
invisibles = [];
}
const lolCat = new Cat();
lolCat.length = "wat";
lolCat.canhaz = false;
lolCat.location['ceiling'] = -1;
lolCat.invisibles.push(0);