Consistently ignore attributes with hyphenated names in JSX (#44873)

* Consistently skip attributes with hyphenated names in JSX

* Add regression test

* Accept new baselines

* Fix tests

* Accept new baselines
This commit is contained in:
Anders Hejlsberg
2021-07-06 13:29:51 -07:00
committed by GitHub
parent 1da18c60d6
commit 8e01a86c01
10 changed files with 227 additions and 24 deletions

View File

@@ -16794,7 +16794,7 @@ namespace ts {
function *generateJsxAttributes(node: JsxAttributes): ElaborationIterator {
if (!length(node.properties)) return;
for (const prop of node.properties) {
if (isJsxSpreadAttribute(prop)) continue;
if (isJsxSpreadAttribute(prop) || isHyphenatedJsxName(idText(prop.name))) continue;
yield { errorNode: prop.name, innerExpression: prop.initializer, nameType: getStringLiteralType(idText(prop.name)) };
}
}
@@ -17355,7 +17355,7 @@ namespace ts {
}
function isIgnoredJsxProperty(source: Type, sourceProp: Symbol) {
return getObjectFlags(source) & ObjectFlags.JsxAttributes && !isUnhyphenatedJsxName(sourceProp.escapedName);
return getObjectFlags(source) & ObjectFlags.JsxAttributes && isHyphenatedJsxName(sourceProp.escapedName);
}
function getNormalizedType(type: Type, writing: boolean): Type {
@@ -26585,8 +26585,8 @@ namespace ts {
return getJsxElementTypeAt(node) || anyType;
}
function isUnhyphenatedJsxName(name: string | __String) {
return !stringContains(name as string, "-");
function isHyphenatedJsxName(name: string | __String) {
return stringContains(name as string, "-");
}
/**
@@ -27127,7 +27127,7 @@ namespace ts {
if (getPropertyOfObjectType(targetType, name) ||
getApplicableIndexInfoForName(targetType, name) ||
isLateBoundName(name) && getIndexInfoOfType(targetType, stringType) ||
isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
isComparingJsxAttributes && isHyphenatedJsxName(name)) {
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
return true;
}

View File

@@ -0,0 +1,32 @@
tests/cases/compiler/ignoredJsxAttributes.tsx(16,5): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/compiler/ignoredJsxAttributes.tsx(20,11): error TS2741: Property 'foo' is missing in type '{ bar: string; "data-yadda": number; }' but required in type 'Props'.
==== tests/cases/compiler/ignoredJsxAttributes.tsx (2 errors) ====
/// <reference path="/.lib/react16.d.ts" />
// Repro from #44797
import * as React from "react";
interface Props {
foo: string;
[dataProp: string]: string;
}
declare function Yadda(props: Props): JSX.Element;
let props: Props = {
foo: "",
"data-yadda": 42, // Error
~~~~~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! related TS6501 tests/cases/compiler/ignoredJsxAttributes.tsx:9:5: The expected type comes from this index signature.
};
let x1 = <Yadda foo="hello" data-yadda={42}/>;
let x2 = <Yadda bar="hello" data-yadda={42}/>; // Error
~~~~~
!!! error TS2741: Property 'foo' is missing in type '{ bar: string; "data-yadda": number; }' but required in type 'Props'.
!!! related TS2728 tests/cases/compiler/ignoredJsxAttributes.tsx:8:5: 'foo' is declared here.

View File

@@ -0,0 +1,35 @@
//// [ignoredJsxAttributes.tsx]
/// <reference path="/.lib/react16.d.ts" />
// Repro from #44797
import * as React from "react";
interface Props {
foo: string;
[dataProp: string]: string;
}
declare function Yadda(props: Props): JSX.Element;
let props: Props = {
foo: "",
"data-yadda": 42, // Error
};
let x1 = <Yadda foo="hello" data-yadda={42}/>;
let x2 = <Yadda bar="hello" data-yadda={42}/>; // Error
//// [ignoredJsxAttributes.js]
"use strict";
/// <reference path="react16.d.ts" />
exports.__esModule = true;
// Repro from #44797
var React = require("react");
var props = {
foo: "",
"data-yadda": 42
};
var x1 = React.createElement(Yadda, { foo: "hello", "data-yadda": 42 });
var x2 = React.createElement(Yadda, { bar: "hello", "data-yadda": 42 }); // Error

View File

@@ -0,0 +1,49 @@
=== tests/cases/compiler/ignoredJsxAttributes.tsx ===
/// <reference path="react16.d.ts" />
// Repro from #44797
import * as React from "react";
>React : Symbol(React, Decl(ignoredJsxAttributes.tsx, 4, 6))
interface Props {
>Props : Symbol(Props, Decl(ignoredJsxAttributes.tsx, 4, 31))
foo: string;
>foo : Symbol(Props.foo, Decl(ignoredJsxAttributes.tsx, 6, 17))
[dataProp: string]: string;
>dataProp : Symbol(dataProp, Decl(ignoredJsxAttributes.tsx, 8, 5))
}
declare function Yadda(props: Props): JSX.Element;
>Yadda : Symbol(Yadda, Decl(ignoredJsxAttributes.tsx, 9, 1))
>props : Symbol(props, Decl(ignoredJsxAttributes.tsx, 11, 23))
>Props : Symbol(Props, Decl(ignoredJsxAttributes.tsx, 4, 31))
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12))
>Element : Symbol(JSX.Element, Decl(react16.d.ts, 2494, 23))
let props: Props = {
>props : Symbol(props, Decl(ignoredJsxAttributes.tsx, 13, 3))
>Props : Symbol(Props, Decl(ignoredJsxAttributes.tsx, 4, 31))
foo: "",
>foo : Symbol(foo, Decl(ignoredJsxAttributes.tsx, 13, 20))
"data-yadda": 42, // Error
>"data-yadda" : Symbol("data-yadda", Decl(ignoredJsxAttributes.tsx, 14, 12))
};
let x1 = <Yadda foo="hello" data-yadda={42}/>;
>x1 : Symbol(x1, Decl(ignoredJsxAttributes.tsx, 18, 3))
>Yadda : Symbol(Yadda, Decl(ignoredJsxAttributes.tsx, 9, 1))
>foo : Symbol(foo, Decl(ignoredJsxAttributes.tsx, 18, 15))
>data-yadda : Symbol(data-yadda, Decl(ignoredJsxAttributes.tsx, 18, 27))
let x2 = <Yadda bar="hello" data-yadda={42}/>; // Error
>x2 : Symbol(x2, Decl(ignoredJsxAttributes.tsx, 19, 3))
>Yadda : Symbol(Yadda, Decl(ignoredJsxAttributes.tsx, 9, 1))
>bar : Symbol(bar, Decl(ignoredJsxAttributes.tsx, 19, 15))
>data-yadda : Symbol(data-yadda, Decl(ignoredJsxAttributes.tsx, 19, 27))

View File

@@ -0,0 +1,51 @@
=== tests/cases/compiler/ignoredJsxAttributes.tsx ===
/// <reference path="react16.d.ts" />
// Repro from #44797
import * as React from "react";
>React : typeof React
interface Props {
foo: string;
>foo : string
[dataProp: string]: string;
>dataProp : string
}
declare function Yadda(props: Props): JSX.Element;
>Yadda : (props: Props) => JSX.Element
>props : Props
>JSX : any
let props: Props = {
>props : Props
>{ foo: "", "data-yadda": 42, // Error} : { foo: string; "data-yadda": number; }
foo: "",
>foo : string
>"" : ""
"data-yadda": 42, // Error
>"data-yadda" : number
>42 : 42
};
let x1 = <Yadda foo="hello" data-yadda={42}/>;
>x1 : JSX.Element
><Yadda foo="hello" data-yadda={42}/> : JSX.Element
>Yadda : (props: Props) => JSX.Element
>foo : string
>data-yadda : number
>42 : 42
let x2 = <Yadda bar="hello" data-yadda={42}/>; // Error
>x2 : JSX.Element
><Yadda bar="hello" data-yadda={42}/> : JSX.Element
>Yadda : (props: Props) => JSX.Element
>bar : string
>data-yadda : number
>42 : 42

View File

@@ -1,4 +1,6 @@
tests/cases/conformance/jsx/file.tsx(9,8): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(9,2): error TS2322: Type '{ "data-foo": number; }' is not assignable to type '{ "data-foo"?: string; }'.
Types of property '"data-foo"' are incompatible.
Type 'number' is not assignable to type 'string'.
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
@@ -11,9 +13,10 @@ tests/cases/conformance/jsx/file.tsx(9,8): error TS2322: Type 'number' is not as
// Error
<test1 data-foo={32} />;
~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:4:12: The expected type comes from property 'data-foo' which is declared here on type '{ "data-foo"?: string; }'
~~~~~
!!! error TS2322: Type '{ "data-foo": number; }' is not assignable to type '{ "data-foo"?: string; }'.
!!! error TS2322: Types of property '"data-foo"' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
// OK
<test1 data-foo={'32'} />;

View File

@@ -31,9 +31,11 @@ tests/cases/conformance/jsx/file.tsx(17,13): error TS2769: No overload matches t
Type '{ yy: boolean; yy1: string; }' is not assignable to type '{ yy: number; yy1: string; }'.
Types of property 'yy' are incompatible.
Type 'boolean' is not assignable to type 'number'.
tests/cases/conformance/jsx/file.tsx(25,12): error TS2769: No overload matches this call.
tests/cases/conformance/jsx/file.tsx(25,13): error TS2769: No overload matches this call.
Overload 1 of 2, '(j: { "extra-data": string; }): Element', gave the following error.
Type 'boolean' is not assignable to type 'string'.
Type '{ "extra-data": true; }' is not assignable to type '{ "extra-data": string; }'.
Types of property '"extra-data"' are incompatible.
Type 'boolean' is not assignable to type 'string'.
Overload 2 of 2, '(n: { yy: string; direction?: number; }): Element', gave the following error.
Property 'yy' is missing in type '{ "extra-data": true; }' but required in type '{ yy: string; direction?: number; }'.
tests/cases/conformance/jsx/file.tsx(26,12): error TS2769: No overload matches this call.
@@ -142,13 +144,14 @@ tests/cases/conformance/jsx/file.tsx(36,12): error TS2769: No overload matches t
// Error
const d1 = <TestingOneThing extra-data />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(j: { "extra-data": string; }): Element', gave the following error.
!!! error TS2769: Type 'boolean' is not assignable to type 'string'.
!!! error TS2769: Type '{ "extra-data": true; }' is not assignable to type '{ "extra-data": string; }'.
!!! error TS2769: Types of property '"extra-data"' are incompatible.
!!! error TS2769: Type 'boolean' is not assignable to type 'string'.
!!! error TS2769: Overload 2 of 2, '(n: { yy: string; direction?: number; }): Element', gave the following error.
!!! error TS2769: Property 'yy' is missing in type '{ "extra-data": true; }' but required in type '{ yy: string; direction?: number; }'.
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:21:38: The expected type comes from property 'extra-data' which is declared here on type 'IntrinsicAttributes & { "extra-data": string; }'
!!! related TS2728 tests/cases/conformance/jsx/file.tsx:22:38: 'yy' is declared here.
const d2 = <TestingOneThing yy="hello" direction="left" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -22,13 +22,15 @@ tests/cases/conformance/jsx/file.tsx(55,68): error TS2769: No overload matches t
Type 'boolean' is not assignable to type 'string'.
Overload 3 of 3, '(hyphenProps: HyphenProps): Element', gave the following error.
Type 'boolean' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(56,12): error TS2769: No overload matches this call.
tests/cases/conformance/jsx/file.tsx(56,13): error TS2769: No overload matches this call.
Overload 1 of 3, '(buttonProps: ButtonProps): Element', gave the following error.
Property 'onClick' is missing in type '{ "data-format": true; }' but required in type 'ButtonProps'.
Overload 2 of 3, '(linkProps: LinkProps): Element', gave the following error.
Property 'to' is missing in type '{ "data-format": true; }' but required in type 'LinkProps'.
Overload 3 of 3, '(hyphenProps: HyphenProps): Element', gave the following error.
Type 'boolean' is not assignable to type 'string'.
Type '{ "data-format": true; }' is not assignable to type 'HyphenProps'.
Types of property '"data-format"' are incompatible.
Type 'boolean' is not assignable to type 'string'.
==== tests/cases/conformance/jsx/file.tsx (4 errors) ====
@@ -122,14 +124,15 @@ tests/cases/conformance/jsx/file.tsx(56,12): error TS2769: No overload matches t
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:5:5: The expected type comes from property 'className' which is declared here on type 'IntrinsicAttributes & LinkProps'
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:5:5: The expected type comes from property 'className' which is declared here on type 'IntrinsicAttributes & HyphenProps'
const b8 = <MainButton data-format />; // incorrect type for specified hyphanated name
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 3, '(buttonProps: ButtonProps): Element', gave the following error.
!!! error TS2769: Property 'onClick' is missing in type '{ "data-format": true; }' but required in type 'ButtonProps'.
!!! error TS2769: Overload 2 of 3, '(linkProps: LinkProps): Element', gave the following error.
!!! error TS2769: Property 'to' is missing in type '{ "data-format": true; }' but required in type 'LinkProps'.
!!! error TS2769: Overload 3 of 3, '(hyphenProps: HyphenProps): Element', gave the following error.
!!! error TS2769: Type 'boolean' is not assignable to type 'string'.
!!! error TS2769: Type '{ "data-format": true; }' is not assignable to type 'HyphenProps'.
!!! error TS2769: Types of property '"data-format"' are incompatible.
!!! error TS2769: Type 'boolean' is not assignable to type 'string'.
!!! related TS2728 tests/cases/conformance/jsx/file.tsx:9:5: 'onClick' is declared here.
!!! related TS2728 tests/cases/conformance/jsx/file.tsx:13:5: 'to' is declared here.
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:17:5: The expected type comes from property 'data-format' which is declared here on type 'IntrinsicAttributes & HyphenProps'
!!! related TS2728 tests/cases/conformance/jsx/file.tsx:13:5: 'to' is declared here.

View File

@@ -1,4 +1,7 @@
tests/cases/conformance/jsx/file.tsx(8,43): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(8,15): error TS2322: Type 'T & { "ignore-prop": number; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'.
Type 'T & { "ignore-prop": number; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'.
Types of property '"ignore-prop"' are incompatible.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(13,15): error TS2322: Type 'T' is not assignable to type 'IntrinsicAttributes & { prop: unknown; "ignore-prop": string; }'.
Type 'T' is not assignable to type '{ prop: unknown; "ignore-prop": string; }'.
tests/cases/conformance/jsx/file.tsx(20,19): error TS2322: Type '(a: number, b: string) => void' is not assignable to type '(arg: number) => void'.
@@ -16,9 +19,11 @@ tests/cases/conformance/jsx/file.tsx(31,52): error TS2322: Type '(val: string) =
// Error
function Bar<T extends {prop: number}>(arg: T) {
let a1 = <ComponentSpecific1 {...arg} ignore-prop={10} />;
~~~~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:53: The expected type comes from property 'ignore-prop' which is declared here on type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'
~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'T & { "ignore-prop": number; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'.
!!! error TS2322: Type 'T & { "ignore-prop": number; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'.
!!! error TS2322: Types of property '"ignore-prop"' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
}
// Error

View File

@@ -0,0 +1,22 @@
// @strict: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
// Repro from #44797
import * as React from "react";
interface Props {
foo: string;
[dataProp: string]: string;
}
declare function Yadda(props: Props): JSX.Element;
let props: Props = {
foo: "",
"data-yadda": 42, // Error
};
let x1 = <Yadda foo="hello" data-yadda={42}/>;
let x2 = <Yadda bar="hello" data-yadda={42}/>; // Error