mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Move JSX props support check and make syntactic (#22970)
* Move JSX props support check and make syntactic * Make parameter required
This commit is contained in:
parent
e4a73f3981
commit
51d44b6097
@ -14767,7 +14767,7 @@ namespace ts {
|
||||
return propsType;
|
||||
}
|
||||
|
||||
function getJsxPropsTypeFromClassType(hostClassType: Type, isJs: boolean, context: Node) {
|
||||
function getJsxPropsTypeFromClassType(hostClassType: Type, isJs: boolean, context: JsxOpeningLikeElement, reportErrors: boolean) {
|
||||
if (isTypeAny(hostClassType)) {
|
||||
return hostClassType;
|
||||
}
|
||||
@ -14786,6 +14786,9 @@ namespace ts {
|
||||
|
||||
if (!attributesType) {
|
||||
// There is no property named 'props' on this instance type
|
||||
if (reportErrors && !!length(context.attributes.properties)) {
|
||||
error(context, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(propsName));
|
||||
}
|
||||
return emptyObjectType;
|
||||
}
|
||||
else if (isTypeAny(attributesType)) {
|
||||
@ -14816,10 +14819,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getJsxPropsTypeFromConstructSignature(sig: Signature, isJs: boolean, context: Node) {
|
||||
function getJsxPropsTypeFromConstructSignature(sig: Signature, isJs: boolean, context: JsxOpeningLikeElement) {
|
||||
const hostClassType = getReturnTypeOfSignature(sig);
|
||||
if (hostClassType) {
|
||||
return getJsxPropsTypeFromClassType(hostClassType, isJs, context);
|
||||
return getJsxPropsTypeFromClassType(hostClassType, isJs, context, /*reportErrors*/ false);
|
||||
}
|
||||
return getJsxPropsTypeFromCallSignature(sig, context);
|
||||
}
|
||||
@ -15857,7 +15860,7 @@ namespace ts {
|
||||
checkTypeRelatedTo(elemInstanceType, elementClassType, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
|
||||
}
|
||||
|
||||
return getJsxPropsTypeFromClassType(elemInstanceType, isInJavaScriptFile(openingLikeElement), openingLikeElement);
|
||||
return getJsxPropsTypeFromClassType(elemInstanceType, isInJavaScriptFile(openingLikeElement), openingLikeElement, /*reportErrors*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -16062,28 +16065,21 @@ namespace ts {
|
||||
// attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes".
|
||||
const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement, checkMode);
|
||||
|
||||
// If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
|
||||
// but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
|
||||
if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || getPropertiesOfType(<ResolvedType>sourceAttributesType).length > 0)) {
|
||||
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(getJsxElementPropertiesName(getJsxNamespaceAt(openingLikeElement))));
|
||||
}
|
||||
else {
|
||||
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
|
||||
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
|
||||
// This will allow excess properties in spread type as it is very common pattern to spread outer attributes into React component in its render method.
|
||||
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
|
||||
for (const attribute of openingLikeElement.attributes.properties) {
|
||||
if (!isJsxAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
const attrName = attribute.name;
|
||||
const isNotIgnoredJsxProperty = (isUnhyphenatedJsxName(idText(attrName)) || !!(getPropertyOfType(targetAttributesType, attrName.escapedText)));
|
||||
if (isNotIgnoredJsxProperty && !isKnownProperty(targetAttributesType, attrName.escapedText, /*isComparingJsxAttributes*/ true)) {
|
||||
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attrName), typeToString(targetAttributesType));
|
||||
// We break here so that errors won't be cascading
|
||||
break;
|
||||
}
|
||||
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
|
||||
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
|
||||
// This will allow excess properties in spread type as it is very common pattern to spread outer attributes into React component in its render method.
|
||||
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
|
||||
for (const attribute of openingLikeElement.attributes.properties) {
|
||||
if (!isJsxAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
const attrName = attribute.name;
|
||||
const isNotIgnoredJsxProperty = (isUnhyphenatedJsxName(idText(attrName)) || !!(getPropertyOfType(targetAttributesType, attrName.escapedText)));
|
||||
if (isNotIgnoredJsxProperty && !isKnownProperty(targetAttributesType, attrName.escapedText, /*isComparingJsxAttributes*/ true)) {
|
||||
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attrName), typeToString(targetAttributesType));
|
||||
// We break here so that errors won't be cascading
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
tests/cases/conformance/jsx/file.tsx(23,1): error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
tests/cases/conformance/jsx/file.tsx(23,7): error TS2339: Property 'x' does not exist on type '{}'.
|
||||
tests/cases/conformance/jsx/file.tsx(25,1): error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
tests/cases/conformance/jsx/file.tsx(26,1): error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
tests/cases/conformance/jsx/file.tsx(33,7): error TS2322: Type '{ x: string; }' is not assignable to type '{ x: number; }'.
|
||||
Types of property 'x' are incompatible.
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsx/file.tsx (3 errors) ====
|
||||
==== tests/cases/conformance/jsx/file.tsx (5 errors) ====
|
||||
declare module JSX {
|
||||
interface Element { }
|
||||
interface ElementAttributesProperty { pr: any; }
|
||||
@ -31,11 +33,15 @@ tests/cases/conformance/jsx/file.tsx(33,7): error TS2322: Type '{ x: string; }'
|
||||
<Obj3 x={10} />; // Error
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'x' does not exist on type '{}'.
|
||||
var attributes: any;
|
||||
<Obj3 {...attributes} />; // Error
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
<Obj3 {...{}} />; // OK
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS2607: JSX element class does not support attributes because it does not have a 'pr' property.
|
||||
|
||||
interface Obj4type {
|
||||
new(n: string): { x: number; pr: { x: number; } };
|
||||
|
||||
14
tests/baselines/reference/tsxNoTypeAnnotatedSFC.errors.txt
Normal file
14
tests/baselines/reference/tsxNoTypeAnnotatedSFC.errors.txt
Normal file
@ -0,0 +1,14 @@
|
||||
tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx(2,24): error TS2307: Cannot find module 'react'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx (1 errors) ====
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
import * as React from 'react';
|
||||
~~~~~~~
|
||||
!!! error TS2307: Cannot find module 'react'.
|
||||
|
||||
const Test123 = () => <div/>;
|
||||
|
||||
function testComponent(props) {
|
||||
return <Test123 {...props}/>;
|
||||
}
|
||||
19
tests/baselines/reference/tsxNoTypeAnnotatedSFC.js
Normal file
19
tests/baselines/reference/tsxNoTypeAnnotatedSFC.js
Normal file
@ -0,0 +1,19 @@
|
||||
//// [tsxNoTypeAnnotatedSFC.tsx]
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
import * as React from 'react';
|
||||
|
||||
const Test123 = () => <div/>;
|
||||
|
||||
function testComponent(props) {
|
||||
return <Test123 {...props}/>;
|
||||
}
|
||||
|
||||
//// [tsxNoTypeAnnotatedSFC.jsx]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
var React = require("react");
|
||||
var Test123 = function () { return <div />; };
|
||||
function testComponent(props) {
|
||||
return <Test123 {...props}/>;
|
||||
}
|
||||
16
tests/baselines/reference/tsxNoTypeAnnotatedSFC.symbols
Normal file
16
tests/baselines/reference/tsxNoTypeAnnotatedSFC.symbols
Normal file
@ -0,0 +1,16 @@
|
||||
=== tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx ===
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
import * as React from 'react';
|
||||
>React : Symbol(React, Decl(tsxNoTypeAnnotatedSFC.tsx, 1, 6))
|
||||
|
||||
const Test123 = () => <div/>;
|
||||
>Test123 : Symbol(Test123, Decl(tsxNoTypeAnnotatedSFC.tsx, 3, 5))
|
||||
|
||||
function testComponent(props) {
|
||||
>testComponent : Symbol(testComponent, Decl(tsxNoTypeAnnotatedSFC.tsx, 3, 29))
|
||||
>props : Symbol(props, Decl(tsxNoTypeAnnotatedSFC.tsx, 5, 23))
|
||||
|
||||
return <Test123 {...props}/>;
|
||||
>Test123 : Symbol(Test123, Decl(tsxNoTypeAnnotatedSFC.tsx, 3, 5))
|
||||
>props : Symbol(props, Decl(tsxNoTypeAnnotatedSFC.tsx, 5, 23))
|
||||
}
|
||||
20
tests/baselines/reference/tsxNoTypeAnnotatedSFC.types
Normal file
20
tests/baselines/reference/tsxNoTypeAnnotatedSFC.types
Normal file
@ -0,0 +1,20 @@
|
||||
=== tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx ===
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
import * as React from 'react';
|
||||
>React : any
|
||||
|
||||
const Test123 = () => <div/>;
|
||||
>Test123 : () => any
|
||||
>() => <div/> : () => any
|
||||
><div/> : any
|
||||
>div : any
|
||||
|
||||
function testComponent(props) {
|
||||
>testComponent : (props: any) => any
|
||||
>props : any
|
||||
|
||||
return <Test123 {...props}/>;
|
||||
><Test123 {...props}/> : any
|
||||
>Test123 : () => any
|
||||
>props : any
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
tests/cases/conformance/jsx/file.tsx(18,21): error TS2607: JSX element class does not support attributes because it does not have a 'props' property.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface Element {}
|
||||
interface ElementAttributesProperty { props: {} }
|
||||
}
|
||||
}
|
||||
declare var React: any;
|
||||
|
||||
export class Empty extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return <div>Hello</div>;
|
||||
}
|
||||
}
|
||||
|
||||
declare const obj: { a: number | undefined } | undefined;
|
||||
|
||||
// OK
|
||||
let unionedSpread = <Empty {...obj} />;
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2607: JSX element class does not support attributes because it does not have a 'props' property.
|
||||
|
||||
9
tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx
Normal file
9
tests/cases/compiler/tsxNoTypeAnnotatedSFC.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
// @jsx: preserve
|
||||
// not _actually_ making react available in this test to regression test #22948
|
||||
import * as React from 'react';
|
||||
|
||||
const Test123 = () => <div/>;
|
||||
|
||||
function testComponent(props) {
|
||||
return <Test123 {...props}/>;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user