Compare commits
3 commits
main
...
contextual
Author | SHA1 | Date | |
---|---|---|---|
4f84639e47 | |||
29c18058d6 | |||
7e4715dfdc |
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// @strictNullChecks: true
|
||||
|
||||
interface X {
|
||||
n: number
|
||||
}
|
||||
class C implements X {
|
||||
n = undefined;
|
||||
}
|
||||
class C2 implements X {
|
||||
n = null;
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
Loading…
Reference in a new issue