Skip to content

Commit 8c9bdf2

Browse files
author
Kanchalai Tanglertsampan
committed
wip-resovle custom JSX attributes type
1 parent cd32221 commit 8c9bdf2

File tree

2 files changed

+69
-47
lines changed

2 files changed

+69
-47
lines changed

src/compiler/checker.ts

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10786,11 +10786,6 @@ namespace ts {
1078610786
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag;
1078710787
return result;
1078810788
}
10789-
10790-
function resolveCustomJsxElementAttributesTypeFromOpeningLikeElement(openingLikeElement: JsxOpeningLikeElement) {
10791-
const symbolTable = getAttributesSymbolTableOfJsxOpeningLikeElement(openingLikeElement);
10792-
return createJsxAttributesType(openingLikeElement.attributes, symbolTable);
10793-
}
1079410789

1079510790
/**
1079610791
* Check attributes type of intrinsic JSx opening-like element.
@@ -10827,39 +10822,21 @@ namespace ts {
1082710822
checkAttributesTypeOfIntrinsicJsxOpeningLikeElement(openingLikeElement);
1082810823
}
1082910824
else {
10830-
const targetAttributesType = getAttributesTypeFromJsxOpeningLikeElement(openingLikeElement);
10831-
const sourceAttribtuesType = resolveCustomJsxElementAttributesTypeFromOpeningLikeElement(openingLikeElement);
10832-
const nameTable = createMap<boolean>();
10833-
// Process this array in right-to-left order so we know which
10834-
// attributes (mostly from spreads) are being overwritten and
10835-
// thus should have their types ignored
10836-
let sawSpreadedAny = false;
10837-
const attributes = openingLikeElement.attributes.properties;
10838-
for (let i = attributes.length - 1; i >= 0; i--) {
10839-
if (attributes[i].kind === SyntaxKind.JsxAttribute) {
10840-
checkJsxAttribute(<JsxAttribute>(attributes[i]), targetAttributesType, nameTable);
10841-
}
10842-
else {
10843-
Debug.assert(attributes[i].kind === SyntaxKind.JsxSpreadAttribute);
10844-
const spreadType = checkJsxSpreadAttribute(<JsxSpreadAttribute>(attributes[i]), targetAttributesType, nameTable);
10845-
if (isTypeAny(spreadType)) {
10846-
sawSpreadedAny = true;
10847-
}
10848-
}
10849-
}
10850-
10851-
// Check that all required properties have been provided. If an 'any'
10852-
// was spreaded in, though, assume that it provided all required properties
10853-
if (targetAttributesType && !sawSpreadedAny) {
10854-
const targetProperties = getPropertiesOfType(targetAttributesType);
10855-
for (let i = 0; i < targetProperties.length; i++) {
10856-
if (!(targetProperties[i].flags & SymbolFlags.Optional) &&
10857-
!nameTable[targetProperties[i].name]) {
10858-
10859-
error(openingLikeElement, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType));
10825+
const targetAttributesType = getCustomJsxElementAttributesType(openingLikeElement);
10826+
const symbolTable = getAttributesSymbolTableOfJsxOpeningLikeElement(openingLikeElement);
10827+
// Filter out any hyphenated names as those are not play any role in type-checking unless there are corresponding properties in the target type
10828+
let attributesTable: Map<Symbol>;
10829+
let sourceAttributesType = anyType as Type;
10830+
if (symbolTable) {
10831+
attributesTable = createMap<Symbol>();
10832+
for (const key in symbolTable) {
10833+
if (isUnhyphenatedJsxName(key) || getPropertyOfType(targetAttributesType, key)) {
10834+
attributesTable[key] = symbolTable[key];
1086010835
}
1086110836
}
10837+
sourceAttributesType = createJsxAttributesType(openingLikeElement.attributes, attributesTable);
1086210838
}
10839+
checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
1086310840
}
1086410841
}
1086510842

@@ -11037,22 +11014,22 @@ namespace ts {
1103711014
}
1103811015

1103911016
/**
11040-
* Resolve attributes type of the given node. The function is intended to initally be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element.
11041-
* @param openingLikeElement a non-instrinsic JSXOPeningLikeElement
11017+
* Resolve attributes type of the given node. The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element.
11018+
* @param openingLikeElement a non-intrinsic JSXOPeningLikeElement
1104211019
* @param elementType an instance type of the given node
1104311020
* @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global (imported from react.d.ts)
1104411021
* @return attributes'type if able to resolve the type of node
1104511022
* anyType if there is no type ElementAttributesProperty or there is an error
1104611023
* emptyObjectType if there is no "prop" in the element instance type
1104711024
**/
11048-
function resolveJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType?: Type, elementClassType?: Type): Type {
11025+
function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType?: Type, elementClassType?: Type): Type {
1104911026
if (!elementType) {
1105011027
elementType = checkExpression(openingLikeElement.tagName);
1105111028
}
1105211029
if (elementType.flags & TypeFlags.Union) {
1105311030
const types = (<TypeOperatorType>elementType).types;
1105411031
return getUnionType(types.map(type => {
11055-
return resolveJsxElementAttributesType(openingLikeElement, type, elementClassType);
11032+
return resolveCustomJsxElementAttributesType(openingLikeElement, type, elementClassType);
1105611033
}), /*subtypeReduction*/ true);
1105711034
}
1105811035

@@ -11062,6 +11039,9 @@ namespace ts {
1106211039
}
1106311040
else if (elementType.flags & TypeFlags.StringLiteral) {
1106411041
// If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type
11042+
// For example:
11043+
// var CustomTag: "h1" = "h1";
11044+
// <CustomTag> Hello World </CustomTag>
1106511045
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
1106611046
if (intrinsicElementsType !== unknownType) {
1106711047
const stringLiteralTypeName = (<LiteralType>elementType).text;
@@ -11086,8 +11066,8 @@ namespace ts {
1108611066
// Is this is a stateless function component? See if its single signature's return type is
1108711067
// assignable to the JSX Element Type
1108811068
if (jsxElementType) {
11089-
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
11090-
const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0];
11069+
// TODO (yuisu) : comment
11070+
const callSignature = getResolvedSignature(openingLikeElement);
1109111071
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
1109211072
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
1109311073
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
@@ -11185,14 +11165,14 @@ namespace ts {
1118511165

1118611166
/**
1118711167
* Get attributes type of the given custom opening-like Jsx element.
11188-
* The function is intended to be called from a function which has handle intrinsic Jsx element.
11168+
* The function is intended to be called from a function which has handle intrinsic Jsx element already.
1118911169
* @param node a custom Jsx opening-like element
1119011170
*/
11191-
function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement): Type {
11171+
function getCustomJsxElementAttributesType(node: JsxOpeningElement): Type {
1119211172
const links = getNodeLinks(node);
1119311173
if (!links.resolvedJsxElementAttributesType) {
1119411174
const elemClassType = getJsxGlobalElementClassType();
11195-
return links.resolvedJsxElementAttributesType = resolveJsxElementAttributesType(node, undefined, elemClassType);
11175+
return links.resolvedJsxElementAttributesType = resolveCustomJsxElementAttributesType(node, undefined, elemClassType);
1119611176
}
1119711177
return links.resolvedJsxElementAttributesType;
1119811178
}
@@ -11787,6 +11767,11 @@ namespace ts {
1178711767
let isDecorator: boolean;
1178811768
let spreadArgIndex = -1;
1178911769

11770+
/// TODO(Yuisu): comment
11771+
if (node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement) {
11772+
return true;
11773+
}
11774+
1179011775
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
1179111776
const tagExpression = <TaggedTemplateExpression>node;
1179211777

@@ -12068,6 +12053,9 @@ namespace ts {
1206812053
// `getEffectiveArgumentCount` and `getEffectiveArgumentType` below.
1206912054
return undefined;
1207012055
}
12056+
else if (node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement) {
12057+
args = [(<JsxOpeningLikeElement>node).attributes];
12058+
}
1207112059
else {
1207212060
args = (<CallExpression>node).arguments || emptyArray;
1207312061
}
@@ -12311,6 +12299,14 @@ namespace ts {
1231112299
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
1231212300
return getGlobalTemplateStringsArrayType();
1231312301
}
12302+
else if (node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement) {
12303+
const symbolTable = getAttributesSymbolTableOfJsxOpeningLikeElement(node as JsxOpeningLikeElement);
12304+
let argAttributesType = anyType as Type;
12305+
if (symbolTable) {
12306+
argAttributesType = createJsxAttributesType((<JsxOpeningLikeElement>node).attributes, symbolTable);
12307+
}
12308+
return argAttributesType;
12309+
}
1231412310

1231512311
// This is not a synthetic argument, so we return 'undefined'
1231612312
// to signal that the caller needs to check the argument.
@@ -12350,10 +12346,11 @@ namespace ts {
1235012346
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature {
1235112347
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
1235212348
const isDecorator = node.kind === SyntaxKind.Decorator;
12349+
const isJsxOpeningLikeElement = node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement;
1235312350

1235412351
let typeArguments: TypeNode[];
1235512352

12356-
if (!isTaggedTemplate && !isDecorator) {
12353+
if (!isTaggedTemplate && !isDecorator && !isJsxOpeningLikeElement) {
1235712354
typeArguments = (<CallExpression>node).typeArguments;
1235812355

1235912356
// We already perform checking on the type arguments on the class declaration itself.
@@ -12465,7 +12462,8 @@ namespace ts {
1246512462
// in arguments too early. If possible, we'd like to only type them once we know the correct
1246612463
// overload. However, this matters for the case where the call is correct. When the call is
1246712464
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
12468-
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
12465+
// TODO (yuisu): comment
12466+
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ isJsxOpeningLikeElement ? false: true);
1246912467
}
1247012468
else if (candidateForTypeArgumentError) {
1247112469
if (!isTaggedTemplate && !isDecorator && typeArguments) {
@@ -12511,6 +12509,9 @@ namespace ts {
1251112509
return resolveErrorCall(node);
1251212510

1251312511
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
12512+
if (isJsxOpeningLikeElement) {
12513+
return;
12514+
}
1251412515
let errorInfo: DiagnosticMessageChain;
1251512516
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
1251612517
if (headMessage) {
@@ -12862,6 +12863,24 @@ namespace ts {
1286212863
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
1286312864
}
1286412865

12866+
/**
12867+
*
12868+
* @param node
12869+
* @param candidatesOutArray
12870+
*/
12871+
function resolveStateLessJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[]): Signature | undefined {
12872+
const elementType = checkExpression(node.tagName);
12873+
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
12874+
12875+
if (callSignatures && callSignatures.length > 0) {
12876+
let callSignature: Signature;
12877+
callSignature = resolveCall(node, callSignatures, candidatesOutArray) || callSignatures[0];
12878+
return callSignature
12879+
}
12880+
12881+
return undefined;
12882+
}
12883+
1286512884
function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
1286612885
switch (node.kind) {
1286712886
case SyntaxKind.CallExpression:
@@ -12872,6 +12891,9 @@ namespace ts {
1287212891
return resolveTaggedTemplateExpression(<TaggedTemplateExpression>node, candidatesOutArray);
1287312892
case SyntaxKind.Decorator:
1287412893
return resolveDecorator(<Decorator>node, candidatesOutArray);
12894+
case SyntaxKind.JsxOpeningElement:
12895+
case SyntaxKind.JsxSelfClosingElement:
12896+
return resolveStateLessJsxOpeningLikeElement(<JsxOpeningLikeElement>node, candidatesOutArray);
1287512897
}
1287612898
Debug.fail("Branch in 'resolveSignature' should be unreachable.");
1287712899
}

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2562,7 +2562,7 @@ namespace ts {
25622562

25632563
/* @internal */
25642564
// Object literals are initially marked fresh. Freshness disappears following an assignment,
2565-
// before a type assertion, or when when an object literal's type is widened. The regular
2565+
// before a type assertion, or when an object literal's type is widened. The regular
25662566
// version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag.
25672567
export interface FreshObjectLiteralType extends ResolvedType {
25682568
regularType: ResolvedType; // Regular version of fresh type

0 commit comments

Comments
 (0)