Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 42 additions & 11 deletions types/react-blessed/index.d.ts
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-blessed relied on a scoped namespace not existing. It wasn't actually using module augmentation. The changes are required to make it work with actual module augmentation since React now has a scoped JSX namespace

Original file line number Diff line number Diff line change
Expand Up @@ -206,23 +206,17 @@ export type DetailedBlessedProps<E extends Element> = BlessedAttributes<E> & Rea
export interface BlessedIntrinsicElements {
element: DetailedBlessedProps<Element>;
box: DetailedBlessedProps<BoxElement>;
text: DetailedBlessedProps<TextElement>;
line: DetailedBlessedProps<LineElement>;
scrollablebox: DetailedBlessedProps<ScrollableBoxElement>;
scrollabletext: DetailedBlessedProps<ScrollableTextElement>;
bigtext: DetailedBlessedProps<BigTextElement>;
list: DetailedBlessedProps<ListElement>;
filemanager: DetailedBlessedProps<FileManagerElement>;
listtable: DetailedBlessedProps<ListTableElement>;
listbar: DetailedBlessedProps<ListbarElement>;
input: DetailedBlessedProps<InputElement>;
textarea: DetailedBlessedProps<TextareaElement>;
textbox: DetailedBlessedProps<TextboxElement>;
button: DetailedBlessedProps<ButtonElement>;
checkbox: DetailedBlessedProps<CheckboxElement>;
radioset: DetailedBlessedProps<RadioSetElement>;
radiobutton: DetailedBlessedProps<RadioButtonElement>;
table: DetailedBlessedProps<TableElement>;
prompt: DetailedBlessedProps<PromptElement>;
question: DetailedBlessedProps<QuestionElement>;
message: DetailedBlessedProps<MessageElement>;
Expand All @@ -248,6 +242,48 @@ export type BlessedIntrinsicElementsPrefixed = {
// augment react JSX when old JSX transform is used
declare module "react" {
namespace JSX {
interface ButtonHTMLAttributes<T>
extends HTMLAttributes<T>,
Omit<
DetailedBlessedProps<ButtonElement>,
'draggable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'ref' | 'style'
> {}

interface TableHTMLAttributes<T>
extends HTMLAttributes<T>,
Omit<
DetailedBlessedProps<TableElement>,
'border' | 'draggable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'ref' | 'style'
> {}

interface TextareaHTMLAttributes<T>
extends HTMLAttributes<T>,
Omit<
DetailedBlessedProps<TextElement>,
'draggable' | 'fill' | 'focusable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'ref' | 'style'
> {}

interface InputHTMLAttributes<T>
extends HTMLAttributes<T>,
Omit<
DetailedBlessedProps<InputElement>,
'draggable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'ref' | 'style'
> {}

interface SVGLineElementAttributes<T>
extends SVGProps<T>,
Omit<
DetailedBlessedProps<LineElement>,
'focusable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'orientation' | 'ref' | 'style'
> {}

interface SVGTextElementAttributes<T>
extends SVGProps<T>,
Omit<
DetailedBlessedProps<TextElement>,
'fill' | 'focusable' | 'onBlur' | 'onClick' | 'onFocus' | 'onResize' | 'ref' | 'style'
> {}

// set IntrinsicElements to 'react-blessed' elements both with and without
// 'blessed-' prefix
interface IntrinsicElements extends BlessedIntrinsicElementsPrefixed, BlessedIntrinsicElements {}
Expand All @@ -257,16 +293,11 @@ declare module "react" {
// augment react/jsx-runtime JSX when new JSX transform is used
declare module "react/jsx-runtime" {
namespace JSX {
// copy React JSX, otherwise class refs won't type as expected
type IntrinsicAttributes = React.Attributes;
interface IntrinsicClassAttributes<T> extends React.ClassAttributes<T> {}
interface IntrinsicElements extends BlessedIntrinsicElementsPrefixed, BlessedIntrinsicElements {}
}
}
declare module "react/jsx-dev-runtime" {
namespace JSX {
type IntrinsicAttributes = React.Attributes;
interface IntrinsicClassAttributes<T> extends React.ClassAttributes<T> {}
interface IntrinsicElements extends BlessedIntrinsicElementsPrefixed, BlessedIntrinsicElements {}
}
}
3 changes: 2 additions & 1 deletion types/react-blessed/react-blessed-tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ const Box = () => <box style={{ fg: "blue" }} />;
const BlessedBox = () => <blessed-box style={{ fg: "blue" }} />;
const FF: React.FC<ReactBlessed.BlessedIntrinsicElements["box"]> = props => <box {...props} />;

// @ts-expect-error
// Undesired. Should error but we can only augment intrinsic elements.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as in - you can't remove a declared existing intrinsic element? that makes sense but I wonder if react-blessed types couldnt be juggled to allow better types through jsxImportSource (the default case wouldn't be fixable since it relies on messing up the already declared namespaces). I also wonder, how does it even work today - is react-blessed "poisonous" for the project? It seems that it's messing up global~ namespace and thus it might change how a ReactDOM-based file is type-checked in the project. Did I get this right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is on our side since we mix in DOM related things into react. The "intrinsics" should all be moved to react-dom. But I think this would be another breaking changes for projects that don't ever import from react-dom.

Once that's done, react-blessed wouldn't need a change.

It would be really great if could separate react and react-dom properly but this is probably a lot of grunt work and I'm not even sure if it's possible while supporting all the legacy types for the factory functions.

as in - you can't remove a declared existing intrinsic element?

I think so because declaration merging is only additive. We could potentially create an API with which you could essentially remove it but it would be specific to React and feel pretty hacky.

// Should not typecheck once `@types/react` moves DOM intrinsics to `react-dom`.
const Div = () => <div />;

/**
Expand Down
38 changes: 36 additions & 2 deletions types/react/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,9 @@ declare namespace React {
interface SVGProps<T> extends SVGAttributes<T>, ClassAttributes<T> {
}

interface SVGLineElementAttributes<T> extends SVGProps<T> {}
interface SVGTextElementAttributes<T> extends SVGProps<T> {}

interface DOMAttributes<T> {
children?: ReactNode | undefined;
dangerouslySetInnerHTML?: {
Expand Down Expand Up @@ -3099,6 +3102,19 @@ declare namespace React {
*/
componentStack: string;
}

namespace JSX {
interface Element extends GlobalJSXElement {}
interface ElementClass extends GlobalJSXElementClass {}
interface ElementAttributesProperty extends GlobalJSXElementAttributesProperty {}
interface ElementChildrenAttribute extends GlobalJSXElementChildrenAttribute {}

type LibraryManagedAttributes<C, P> = GlobalJSXLibraryManagedAttributes<C, P>;

interface IntrinsicAttributes extends GlobalJSXIntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends GlobalJSXIntrinsicClassAttributes<T> {}
interface IntrinsicElements extends GlobalJSXIntrinsicElements {}
}
}

// naked 'any' type in a conditional type will short circuit and union both the then/else branches
Expand Down Expand Up @@ -3146,6 +3162,9 @@ type ReactManagedAttributes<C, P> = C extends { propTypes: infer T; defaultProps
: P;

declare global {
/**
* @deprecated Use `React.JSX` instead of the global `JSX` namespace.
*/
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
interface ElementClass extends React.Component<any> {
Expand Down Expand Up @@ -3326,7 +3345,7 @@ declare global {
foreignObject: React.SVGProps<SVGForeignObjectElement>;
g: React.SVGProps<SVGGElement>;
image: React.SVGProps<SVGImageElement>;
line: React.SVGProps<SVGLineElement>;
line: React.SVGLineElementAttributes<SVGLineElement>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why this new interface was added? I certainly don't mind it - I'm just curious

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that react-blessed can add attributes. Ultimately, we should just add separate prop interfaces for all elements. I'm just a bit hesitant though because people may not understand what goes in there and when. There's a common confusion around HTML attributes vs DOM properties vs React props.

linearGradient: React.SVGProps<SVGLinearGradientElement>;
marker: React.SVGProps<SVGMarkerElement>;
mask: React.SVGProps<SVGMaskElement>;
Expand All @@ -3341,11 +3360,26 @@ declare global {
stop: React.SVGProps<SVGStopElement>;
switch: React.SVGProps<SVGSwitchElement>;
symbol: React.SVGProps<SVGSymbolElement>;
text: React.SVGProps<SVGTextElement>;
text: React.SVGTextElementAttributes<SVGTextElement>;
textPath: React.SVGProps<SVGTextPathElement>;
tspan: React.SVGProps<SVGTSpanElement>;
use: React.SVGProps<SVGUseElement>;
view: React.SVGProps<SVGViewElement>;
}
}
}

// React.JSX needs to point to global.JSX to keep global module augmentations intact.
// But we can't access global.JSX so we need to create these aliases instead.
// Once the global JSX namespace will be removed we replace React.JSX with the contents of global.JSX
interface GlobalJSXElement extends JSX.Element {}
interface GlobalJSXElementClass extends JSX.ElementClass {}
interface GlobalJSXElementAttributesProperty extends JSX.ElementAttributesProperty {}
interface GlobalJSXElementChildrenAttribute extends JSX.ElementChildrenAttribute {}

type GlobalJSXLibraryManagedAttributes<C, P> = JSX.LibraryManagedAttributes<C, P>;

interface GlobalJSXIntrinsicAttributes extends JSX.IntrinsicAttributes {}
interface GlobalJSXIntrinsicClassAttributes<T> extends JSX.IntrinsicClassAttributes<T> {}

interface GlobalJSXIntrinsicElements extends JSX.IntrinsicElements {}
14 changes: 12 additions & 2 deletions types/react/jsx-dev-runtime.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// Expose `JSX` namespace in `global` namespace
import './';
import * as React from './';

export namespace JSX {
interface Element extends React.JSX.Element {}
interface ElementClass extends React.JSX.ElementClass {}
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
interface IntrinsicAttributes extends React.JSX.IntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends React.JSX.IntrinsicClassAttributes<T> {}
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
}
14 changes: 12 additions & 2 deletions types/react/jsx-runtime.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// Expose `JSX` namespace in `global` namespace
import './';
import * as React from './';

export namespace JSX {
interface Element extends React.JSX.Element {}
interface ElementClass extends React.JSX.ElementClass {}
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
interface IntrinsicAttributes extends React.JSX.IntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends React.JSX.IntrinsicClassAttributes<T> {}
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
}
18 changes: 16 additions & 2 deletions types/react/test/tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,15 +502,29 @@ const HasHref2: React.ElementType<{ href?: string | undefined }> = 'div';
const CustomElement: React.ElementType = 'my-undeclared-element';

// custom elements now need to be declared as intrinsic elements
declare global {
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'my-declared-element': {};
}
}
}

const CustomElement2: React.ElementType = 'my-declared-element';
// Augmentations of the global namespace flow into the scoped JSX namespace
// This is deprecated and will be removed in next next major of `@types/react`
declare global {
namespace JSX {
interface IntrinsicElements {
'my-declared-element-deprecated': {};
}
}
}

const CustomElement2: React.ElementType = 'my-declared-element-deprecated';
<my-declared-element-deprecated />;

const CustomElement3: React.ElementType = 'my-declared-element';
<my-declared-element />;

interface TestPropTypesProps {
foo: string;
Expand Down
38 changes: 36 additions & 2 deletions types/react/ts5.0/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,9 @@ declare namespace React {
interface SVGProps<T> extends SVGAttributes<T>, ClassAttributes<T> {
}

interface SVGLineElementAttributes<T> extends SVGProps<T> {}
interface SVGTextElementAttributes<T> extends SVGProps<T> {}

interface DOMAttributes<T> {
children?: ReactNode | undefined;
dangerouslySetInnerHTML?: {
Expand Down Expand Up @@ -3045,6 +3048,19 @@ declare namespace React {
*/
componentStack: string;
}

namespace JSX {
interface Element extends GlobalJSXElement {}
interface ElementClass extends GlobalJSXElementClass {}
interface ElementAttributesProperty extends GlobalJSXElementAttributesProperty {}
interface ElementChildrenAttribute extends GlobalJSXElementChildrenAttribute {}

type LibraryManagedAttributes<C, P> = GlobalJSXLibraryManagedAttributes<C, P>;

interface IntrinsicAttributes extends GlobalJSXIntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends GlobalJSXIntrinsicClassAttributes<T> {}
interface IntrinsicElements extends GlobalJSXIntrinsicElements {}
}
}

// naked 'any' type in a conditional type will short circuit and union both the then/else branches
Expand Down Expand Up @@ -3092,6 +3108,9 @@ type ReactManagedAttributes<C, P> = C extends { propTypes: infer T; defaultProps
: P;

declare global {
/**
* @deprecated Use `React.JSX` instead of the global `JSX` namespace.
*/
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
interface ElementClass extends React.Component<any> {
Expand Down Expand Up @@ -3272,7 +3291,7 @@ declare global {
foreignObject: React.SVGProps<SVGForeignObjectElement>;
g: React.SVGProps<SVGGElement>;
image: React.SVGProps<SVGImageElement>;
line: React.SVGProps<SVGLineElement>;
line: React.SVGLineElementAttributes<SVGLineElement>;
linearGradient: React.SVGProps<SVGLinearGradientElement>;
marker: React.SVGProps<SVGMarkerElement>;
mask: React.SVGProps<SVGMaskElement>;
Expand All @@ -3287,11 +3306,26 @@ declare global {
stop: React.SVGProps<SVGStopElement>;
switch: React.SVGProps<SVGSwitchElement>;
symbol: React.SVGProps<SVGSymbolElement>;
text: React.SVGProps<SVGTextElement>;
text: React.SVGTextElementAttributes<SVGTextElement>;
textPath: React.SVGProps<SVGTextPathElement>;
tspan: React.SVGProps<SVGTSpanElement>;
use: React.SVGProps<SVGUseElement>;
view: React.SVGProps<SVGViewElement>;
}
}
}

// React.JSX needs to point to global.JSX to keep global module augmentations intact.
// But we can't access global.JSX so we need to create these aliases instead.
// Once the global JSX namespace will be removed we replace React.JSX with the contents of global.JSX
interface GlobalJSXElement extends JSX.Element {}
interface GlobalJSXElementClass extends JSX.ElementClass {}
interface GlobalJSXElementAttributesProperty extends JSX.ElementAttributesProperty {}
interface GlobalJSXElementChildrenAttribute extends JSX.ElementChildrenAttribute {}

type GlobalJSXLibraryManagedAttributes<C, P> = JSX.LibraryManagedAttributes<C, P>;

interface GlobalJSXIntrinsicAttributes extends JSX.IntrinsicAttributes {}
interface GlobalJSXIntrinsicClassAttributes<T> extends JSX.IntrinsicClassAttributes<T> {}

interface GlobalJSXIntrinsicElements extends JSX.IntrinsicElements {}
14 changes: 12 additions & 2 deletions types/react/ts5.0/jsx-dev-runtime.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// Expose `JSX` namespace in `global` namespace
import './';
import * as React from './';

export namespace JSX {
interface Element extends React.JSX.Element {}
interface ElementClass extends React.JSX.ElementClass {}
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
interface IntrinsicAttributes extends React.JSX.IntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends React.JSX.IntrinsicClassAttributes<T> {}
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
}
14 changes: 12 additions & 2 deletions types/react/ts5.0/jsx-runtime.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// Expose `JSX` namespace in `global` namespace
import './';
import * as React from './';

export namespace JSX {
interface Element extends React.JSX.Element {}
interface ElementClass extends React.JSX.ElementClass {}
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
interface IntrinsicAttributes extends React.JSX.IntrinsicAttributes {}
interface IntrinsicClassAttributes<T> extends React.JSX.IntrinsicClassAttributes<T> {}
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
}
18 changes: 16 additions & 2 deletions types/react/ts5.0/test/tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,15 +502,29 @@ const HasHref2: React.ElementType<{ href?: string | undefined }> = 'div';
const CustomElement: React.ElementType = 'my-undeclared-element';

// custom elements now need to be declared as intrinsic elements
declare global {
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'my-declared-element': {};
}
}
}

const CustomElement2: React.ElementType = 'my-declared-element';
// Augmentations of the global namespace flow into the scoped JSX namespace
// This is deprecated and will be removed in next next major of `@types/react`
declare global {
namespace JSX {
interface IntrinsicElements {
'my-declared-element-deprecated': {};
}
}
}

const CustomElement2: React.ElementType = 'my-declared-element-deprecated';
<my-declared-element-deprecated />;

const CustomElement3: React.ElementType = 'my-declared-element';
<my-declared-element />;

interface TestPropTypesProps {
foo: string;
Expand Down
Loading