TypeScript λ²μ : 3.2.0-rc, 3.2.1
μνΈ
import * as React from "react";
import { Component, ReactType} from "react";
function App(props:{component:ReactType}) {
const Comp: ReactType = props.component
return (<Comp />)
}
μμλλ λμ :
TypeScript 3.1.6μΌλ‘ μ μμ μΌλ‘ μΆλ ₯λμ΄μΌν©λλ€.
μ€μ νλ :
TS2604: JSX element type 'Comp' does not have any construct or call signatures.
μ¬κΈ°μλ λλ¨Έμ§ μ€λ₯ λ©μμ§κ° λλ½λμ΄ μμ§λ§ @types/react
μ μ΅μ λ²μ μ μ¬μ©νκ³ μκ³ node_modules λλ μ κΈ νμΌμ μ€λ³΅ λ λ΄μ©μ΄ λ¨μ μμ§ μμμ§ νμΈνμμμ€.
νΈμ§ : μλ§λ ReactTypeμ΄ μλ ComponentTypeμ μ¬μ©νλ κ²μ μλ―Έν©λλ€.
λΏ‘λΏ‘
μ΅μ @types/react 16.7.7
νκ³ @types/react
μμ yarn.lock
@types/react
μ€λ³΅ νλͺ©μ΄ μλμ§ νμΈνμ΅λλ€.
@types/react
μ€λ³΅μΌλ‘ μΈν΄ λ¬Έμ κ° λ°μν κ²½μ° Duplication of definition ...
μ€λ₯κ° λ°νλ©λλ€.
"typescript": "^3.1.6"
λ‘ λ λ리면 μ μμ μΌλ‘ μλν©λλ€.
νμ΄ν μ€ν¬λ¦½νΈ 3.2.1
λ€λ₯Έ μ
interface P1 {
p?: boolean
c?: string
}
interface P2 {
p?: boolean
c?: any // if you replace c with string, error goes away
d?: any
}
declare var C: React.ComponentType<P1> | React.ComponentType<P2>
const a = <C p={true} /> // element does not have any ...
λ¬Έμ λ μλ‘μ΄ "nullable νλ³"λ³κ²½κ³Ό κ΄λ ¨λ κ² κ°μ΅λλ€. tsκ° ν΄λΉ μ νμ λν κ³΅ν΅ μΈν°νμ΄μ€λ₯Ό μ°Ύμ μμλ κ² κ°μ΅λλ€.
λ€λ₯Έ μ
interface P1 {
p?: any
c?: string // remove this and it's ok
}
interface P2 {
p?: boolean
}
λΉμ·ν λ¬Έμ κ° μμ΅λλ€. λΉ λ₯Έ μ :
import * as React from 'react'
//
// Types
//
interface Config<P> {
ElementType: React.ReactType<P>
}
interface EmptyProps {
}
interface Props {
a?: string
}
type HProps = {
configEmpty: Config<EmptyProps>,
config: Config<Props>
}
//
// Component
//
const H: React.FC<HProps> = ({ config, configEmpty }) => {
const A: React.ReactType<EmptyProps> = configEmpty.ElementType // assigned
const B: React.ReactType<EmptyProps> = 'div' // assigned
const C: React.ReactType<Props> = config.ElementType // assigned
const D: React.ReactType<Props> = 'div' // in this case assignment failed
return (
<div>
<A/> {/* TS2604: JSX element type 'A' does not have any construct or call signatures. */}
<B/>
<C/>
<D/>
</div>
)
}
export default H
A
λ° B
μ μ ν μ΄ B
λ° D
μλ _ μ μ¬ν μ ν _μ΄ μμ§λ§ D
λ ν λΉ ν μ μμ΅λλ€ π€
μ μ₯μμ μ½λ : https://github.com/layershifter/ts-issue
const
μ¬μ©νκ³ μκΈ° λλ¬Έμ κ·Έλ μ§ μμ΅λλ€. A
μ typeof configEmpty.ElementType
μ΄μ§λ§ B
μ 'div'
μ
λλ€. let
μ¬μ©νλ©΄ λ λ€μμ λμΌν λμμ 보μ¬μ€λλ€.
@Kovensky A
λ° C
μ΄λ»μ΅λκΉ? C
μλνμ§λ§ A
κ° does not have any construct
μ€λ₯λ‘ μ€ν¨νλ μ΄μ λ 무μμ
λκΉ?
κ·Έκ²μ λΉμ μ΄ μ 곡νλ μνμλ°μ μμλ κ΅¬μ± μμλ₯Ό μ μΈνλλ‘ ReactType
μ νμ κ°μ νκΈ° λλ¬Έμ
λλ€. μ΄λ€ DOM μμλ { a: string | undefined }
λ°μ μ μκΈ° λλ¬Έμ λͺ¨λ μ μΈλκ³ ν¨μ / ν΄λμ€ κ΅¬μ± μμ λ§ ν λΉ ν μ μμ΅λλ€.
@Kovensky κ°μ¬ν©λλ€ π μ΄μ κ³Όμ μ λν λ¬Έμ λ λΆλͺ νμ§λ§ μ΄κ±΄ μ΄λ¨κΉμ?
import * as React from 'react'
//
// Types
//
interface Config<P> {
ElementType: React.ReactType<P>
}
interface Empty {}
interface Props { a?: string }
type HProps = {
configEmpty: Config<Empty>,
config: Config<Props>
}
//
// Component
//
const H: React.FC<HProps> = ({ config, configEmpty }) => {
const A: React.ReactType<Empty> = configEmpty.ElementType // is React.ReactType<Empty>
const B: React.ReactType<Empty> = 'div' // is string
const C: React.ReactType<Props> = config.ElementType // is React.ReactType<Props>
return (
<div>
{/* React.ReactType<Empty> */} <A/> {/* <--- TS2604: JSX element type 'A' does not have any construct or call signatures. */}
{/* React.ReactType<Empty> */} <B/>
{/* React.ReactType<Props> */} <C/>
</div>
)
}
export default H
A
λ° B
μλ λͺ
λ°±νκ² μ μ λ μ νμ΄ λμΌν©λλ€. A
κ° μ€ν¨νλ μ΄μ λ 무μμ
λκΉ? μ λ§ νΌλ μ€λ¬μμ.
@rexpan κ³Ό @goloveychuk μ μ€λ₯λ μ‘°κ±΄λΆ JSX μμ±μλ₯Ό κΈ°λ°μΌλ‘ # 28795 λ° # 28768κ³Ό μ μ¬ν©λλ€.
ν , κ·Έλμ λ¬Έμ λ ReactType<any>
κ° _every builtin jsx tag _ (+ ComponentType<any>
λν΄ μ£Όμ§λ§ κ·Έ λΆλΆμ λ¬Έμ κ°λμ§ μμ΅λλ€)μ ν©μ§ν©μ λ°ννκ³ , ν΄λΉ κ΅¬μ± μμμ μλͺ
μ΄ κ°λ¨νμ§ μλ€λ κ²μ
λλ€. λ€λ₯Έ λͺ¨λ κ²μ μλ²½νκ² ν¬ν¨νλ μλͺ
μ΄ μλλλ€.) μ΄κ²λ μμ νκΈ° μν΄ # 7294μ μμ‘΄ν©λλ€. νλ¬μ€ μΈ‘λ©΄μμλ³΄κ³ λλ μ΄λ¬ν λͺ¨λ JSX λ¬Έμ λ₯Ό ν΄κ²°νλ λ° νμν κΈ°λ³Έ λ³κ²½ μ¬νμ λμΌν©λλ€.
컨ν μ€νΈλ₯Ό μν΄ λ€μκ³Ό κ°μ μ νμ ν¨κ³Όμ μΌλ‘ μ μ‘°ν©λλ€.
declare const JsxSigs: {[K in keyof JSX.IntrinsicElements]: ((props: JSX.IntrinsicElements[K]) => JSX.Element)}[keyof JSX.IntrinsicElements];
μ΄λ μλ§μ κ³ μ μλͺ μ μ‘°ν©μΌλ‘ λλ©λλ€.
λ΄κ° λͺ¨λ λ΄μ₯ jsx νκ·Έμ ν©μ§ν©μΌλ‘ λ³κ²½νκΈ° μ μλ _just_ string | ComponentType<any>
μλλ°, λ λλΉ΄μ΅λλ€.
μ, νΉν 3.2 μ΄μ λΆν°λ νκ·Έμ string
μ νμ΄ μ‘°μ©ν _disabled_ typecheckerμ
λλ€. μλνλ©΄ λ°νν΄μΌνλ "μΈλ±μ€λ₯Ό μ°Ύμ μ μμ"μ€λ₯κ° λ°μνμ§ μμκΈ° λλ¬Έμ
λλ€.
μ΄ λ¬Έμ λ μ€λ³΅μΌλ‘ νμλμμΌλ©° λ§μ§λ§ λ μλ νλμ΄ μμμ΅λλ€. μλ μ²μλ₯Ό μν΄ νμλμμ΅λλ€.
κ΄λ ¨μ΄μμ μμλ λ¬Έμ κ° λ°μν©λλ€. λ€μ κ΅¬μ± μμκ° μμ΅λλ€.
function MaybeLabel(props: { useLabel: boolean }) {
const { useLabel } = props;
const TagName = useLabel ? 'label' : 'div';
return <TagName>Woookie</TagName>
}
κ²°κ³Όμ μΌλ‘
error TS2604: JSX element type 'TagName' does
μλ have any construct or call signatures.
λμ
function MaybeLabel2(props: { useLabel: boolean }) {
const { useLabel } = props;
const TagName = useLabel ? 'span' : 'div';
return <TagName>Woookie</TagName>
}
typescript μ»΄νμΌλ¬μμ μμ ν νμ©λ©λλ€. κ·Έλλ‘:
export function MaybeLabel3(props: { useLabel: boolean }) {
const { useLabel } = props;
const TagName = useLabel ? 'label' : 'div';
return React.createElement(TagName, 'Wookie')
}
μ μΌν μ°¨μ΄λ μ΄λμμ MaybeLabel2
λ΄κ° μ¬μ©νκ³ μλ€λ κ²μ
λλ€ span
λμ label
(μ¬μ© span
λμ div
λ κ°λ₯ 보μΈλ€ ). MaybeLabel3
λ μ νν MaybeLabel
μ»΄νμΌνλ κ²κ³Ό κ°μμΌνκΈ° λλ¬Έμ λ μ΄μνκ² λ§λλλ€.
μ΅μ λ²μ μ @ types / react λ° @ types / react-domμ μ¬μ©νκ³ typescript 3.2.1, 3.2.2 λ° 3.3.0-dev.20181219μμ λ¬Έμ λ₯Ό νμΈνμ΅λλ€. 3.1.6μμλ λͺ¨λ μμλλ‘ μλν©λλ€ (μμ μ€ μ΄λ κ²λ μ€λ₯λ₯Ό μμ±νμ§ μμ).
λμκ² ν΄κ²°μ± μ λ€μκ³Ό κ°μ΅λλ€.
export type WrapperProps = {
mainApp: React.ElementType
}
@ TacB0sS μμΈν μ€λͺ ν΄μ£ΌμΈμ.
μ€λ λλ₯Ό μ€ν΄νμ μλ μμ§λ§ jsx μμμ λν μ°Έμ‘°λ₯Ό λ€λ₯Έ jsx μμμ μ λ¬νκ³ μΆμμ΅λλ€.
export const AppWrapper = hot(module)((props: WrapperProps) => {
const MainApp = props.mainApp;
if (!MainApp) // <-- JSX elements MUST start with upper case!!
throw new ImplementationMissingException("mainApp was not specified!!");
return (
<Router history={BrowserHistoryModule.getHistory()}>
<MainApp prop1={"value"}/>
</Router>)
});
ν¨κ» :
export type WrapperProps = {
mainApp: React.ElementType<{prop1:string}>
}
@ TacB0sS μ μκ² μ£Όμ νΈλ¦μ if
쑰건μ μΆκ°νλ κ²μ΄ μμ΅λλ€. React.ComponentType
λλ React.ElementType
μ¬μ© μ¬λΆλ μ€μνμ§ μμ κ² κ°μ΅λλ€.
μ΄ κ²°κ³Όλ₯Ό μ΄ν΄νλμ§ μ λͺ¨λ₯΄κ² μ΅λλ€. ν΄κ²°ν μμλ μ μ¬ν μ€λ₯κ° μμ΅λλ€. λ΄ μ¬μ© μ¬λ‘λ λ€μκ³Ό κ°μ΅λλ€.
// Logo.tsx
import classNames from 'classnames';
interface Props extends React.HTMLAttributes<HTMLElement> {
tag?: React.ReactType;
}
const Logo: React.SFC<Props> = props => {
const { tag: Tag = 'div', className, ...rest } = props;
return (
<Tag
className={classNames(styles.logo, className)}
{...rest}
dangerouslySetInnerHTML={{ __html: logo }}
/>
);
};
κ·Έλ° λ€μ λ€λ₯Έ κ΅¬μ± μμμμ λ€μκ³Ό κ°μ΄ μ¬μ©νκ³ μμ΅λλ€.
const Header: React.SFC<{}> = () => {
return (
<div>
<Logo tag="h1" aria-label="Syn By Design: Eric Masiello's Portfolio" />
Header online
</div>
);
};
μ»΄νμΌλ¬λ₯Ό μ€νν λμ΄ μ€λ₯κ° λ°μν©λλ€.
components/Logo.tsx:13:6 - error TS2604: JSX element type 'Tag' does not have any construct or call signatures.
13 <Tag
~~~
Found 1 error.
μ΄ μμ μ μννλ λ°©λ²μ λν μμ΄λμ΄κ° μμ΅λκΉ?
Component
λμ ComponentClass
κΈ°λ³Έκ°μ κ°μ Έ μμ§λ§ ν΄λΉ λ΄λ³΄λ΄κΈ°λ₯Ό .d.ts
νμΌμμ κΈ°λ³Έκ° λμ μ΄λ¦μΌλ‘ μ μΈνκΈ° λλ¬Έμμ΄ μ€λ₯κ° λ°μνμ΅λλ€.
μ¬κΈ°μ μΆκ°νκ³ μΆμμ΅λλ€. μ΄κ²μ΄ μ΄λ―Έ μΈκΈλμλμ§ νμ€νμ§ μμ§λ§ κ³ μ©μ μ£Όλ¬Έ κ΅¬μ± μμλ₯Ό μννλ κ²½μ° React.ComponentType μ νμ μ¬μ©νκ³ μΆμ΅λλ€.
. " https://flow.org/en/docs/react/types/#toc -react-componenttype"
@ericmasiello λμ μΌλ‘ μ λ¬λλ κ΅¬μ± μμμ React.ElementType
λ₯Ό μ¬μ©νμ΅λλ€. λ΄ μ¬μ© μ¬λ‘λ κΈ°λ³Έμ μΌλ‘ λ€μκ³Ό κ°μ΅λλ€.
type Props = {
heading: React.ElementType
}
const Header: FC<Props> = props => {
const Header = props.heading ?? 'h2';
return (
<Header className="some-class"><children /></Header>
)
}
@ericmasiello λμ μΌλ‘ μ λ¬λλ κ΅¬μ± μμμ
React.ElementType
λ₯Ό μ¬μ©νμ΅λλ€. λ΄ μ¬μ© μ¬λ‘λ κΈ°λ³Έμ μΌλ‘ λ€μκ³Ό κ°μ΅λλ€.type Props = { heading: React.ElementType } const Header: FC<Props> = props => { const Header = props.heading ?? 'h2'; return ( <Header className="some-class"><children /></Header> ) }
λ©μλ! μ΄λ€ λ²μ μ Typescriptμ μ΄λ€ λ²μ μ @ types / reactλ₯Ό μ€μΉ νμ΅λκΉ?
μλ νμΈμ.
λ©μλ! μ΄λ€ λ²μ μ Typescriptμ μ΄λ€ λ²μ μ @ types / reactλ₯Ό μ€μΉ νμ΅λκΉ?
[email protected]
@ types / [email protected]
λ€μ λ κ°μ§ μ€ νλλ₯Ό μννμ¬μ΄ λ¬Έμ λ₯Ό ν΄κ²°νμ΅λλ€.
.d.ts νμΌ :
declare module "foo" {
interface PropFoo {
propy: string;
}
class MyTypedComponent extends React.Component<PropFoo> { }
}
λ°μ :
import MyTypedComponent from "foo";
function AnotherComponent() {
/* Notice in here we have to use the dot operator and reference the component */
return <MyTypedComponent.MyTypedComponent />
}
μλ‘ μ λ ₯ λ κ΅¬μ± μμλ₯Ό μ¬μ©νλ €λ©΄ _MyTypedComponent.MyTypedComponent_λ₯Ό μμ±ν΄μΌν©λλ€. μ΄κ²μ μ΄λ€ μ¬λλ€μκ²λ λΆλͺ ν μ μμ§λ§, κ°μ Έ μ€κΈ°μ λνΈ μ°μ°μλ₯Ό μ¬μ©νκ³ κ΅¬μ± μμλ₯Ό μ°Έμ‘°νλ κ²μ΄ μ λΆ μμ λ λ§μ μκ°μ λλΉνμ΅λλ€.
.d.ts νμΌ :
declare module "foo" {
interface PropFoo {
propy: string;
}
export default class MyTypedComponent extends React.Component<PropFoo> { } //Notice the export default in here
}
λ°μ :
import MyTypedComponent from "foo";
function AnotherComponent() {
/* Since this component is default exported, no need to use the dot operator */
return <MyTypedComponent />
}
Soo, κΈ°λ³Έμ μΌλ‘ λ΄λ³΄λ΄κΈ°, κΈ°λ³Έ λ΄λ³΄λ΄κΈ° λ° κ°μ Έ μ€κΈ°λ₯Ό νμΈνκ³ μ¬λ°λ₯΄κ² μ°Έμ‘°νκ³ μλμ§ νμΈνμμμ€.
μ μμ΄μ λν΄ μ κ°μ΄λ©° λμμ΄ λμκΈ°λ₯Ό λ°λλλ€.
λμκ² ν΄κ²°μ± μ λ€μκ³Ό κ°μ΅λλ€.
export type WrapperProps = { mainApp: React.ElementType }
niu b
κ°μ₯ μ μ©ν λκΈ
λμκ² ν΄κ²°μ± μ λ€μκ³Ό κ°μ΅λλ€.