๊ธฐ๋ณธ ์์ฑ์ ํ์ฌ strictNullChecks๊ฐ ํ์ฑํ๋ ์ํ์์ ์ ๋๋ก ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์๋ฅผ ๋ค์ด:
interface TestProps { x?: number}
class Test extends React.Component<TestProps, null> {
static defaultProps = {x: 5};
render() {
const x: number = this.props.x;
return <p>{x}</p>;
}
}
๋ฐํ์ ์ ์๋์ด ๋ณด์ฅ๋์ง๋ง error TS2322: Type 'number | undefined' is not assignable to type 'number'
์ค๋ฅ.
ํ์ฌ defaultProps์ Props๋ ํญ์ ๊ฐ์ ์ ํ์ผ๋ก ์ทจ๊ธ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง Props์ ์ ํ์ ํ๋๋ DefaultProps์ ํ์ ๊ฐ์ผ๋ก ๋ฎ์ด์จ์ผ ํ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก๋ ๊ฑฐ์ ๋์ผํ ์ ํ์ด ์๋๋๋ค.
๋ญ ์ด๋ฐ๊ฒ ์์์ผ๋ฉด...
class ComponentWithDefaultProps<P, D, S> {
props: P & D & {children?: React.Children};
}
props์ ์ ํ์ ์ ์ธํ๊ณ ๊ธฐ์กด React.Component ํ์ดํ๊ณผ ๋์ผํฉ๋๊น?
๊ธฐ๋ณธ props๋ ๋ฐํ์์ ์ค์ ๋๊ธฐ ๋๋ฌธ์ type assertion ์ธ์ ์ด๋ฅผ ์ ์ฒ๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. (๋ฌผ๋ก , ํญ์ ์๊ฒฉํ null ๊ฒ์ฌ๋ฅผ ๋นํ์ฑํํ ์ ์์ต๋๋ค.)
๋ค์์ ๊ทํ์ ์์์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋๋ค.
interface TestProps { x?: number}
class Test extends React.Component<TestProps, null> {
static defaultProps = {x: 5};
render() {
const x: number = (this.props.x as number);
return <p>{x}</p>;
}
}
https://www.typescriptlang.org/docs/handbook/basic-types.html#type -assertions ์ฐธ์กฐ
์ข ๋ ์ฐ์ํ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ด ์๋ค๋ฉด ๋ฃ๊ณ ์ถ์ต๋๋ค.
๋ฉด์ฑ ์กฐํญ: ์ ๋ ์ฝ 3์ผ ๋์ TypeScript๋ฅผ ์ฌ์ฉํด ์์ผ๋ฉฐ ํผ๊ณคํ๋ฉฐ ์๋ง๋ ์ ๊ฐ ๋ฌด์จ ๋ง์ ํ๊ณ ์๋์ง ๋ชจ๋ฅผ ๊ฒ์ ๋๋ค.
3๊ฐ์ ์ผ๋ฐ ์ ํ ์ ์๋ฅผ ํฌํจํ๋๋ก ์ ํ ์ ์ ํ์ผ์ ์ ๋ฐ์ดํธํ ๋ +1000.
์ด๊ฒ์ ---strictNullChecks
์์ด๋ ๊ด์ฐฎ์์ง๋ง ์ง๊ธ์ ๋ง์ ์ปดํฌ๋ํธ ํด๋์ค์์ ํ์คํ ๋ฌธ์ ๊ฐ ๋ ๊ฒ์
๋๋ค.
Flow๋ ์๊ฒฉํ null ์ ํ ๊ฒ์ฌ์ ํน์ฑ ๋๋ฌธ์ ์ ์ฌํ ํด๋์ค ๊ตฌํ๋ ๊ตฌํํฉ๋๋ค.
https://github.com/facebook/flow/blob/master/lib/react.js#L16
https://github.com/facebook/flow/blob/master/lib/react.js#L104 -L105
์ 3์ ์ ๋ค๋ฆญ์ ์ถ๊ฐํ๊ธฐ ์ํด https://github.com/Microsoft/TypeScript/issues/2175 ๊ฐ ํด๊ฒฐ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ ์ธ์๋ ์ต์
์ด ๋ง์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค.
๋๋ ๊ทธ๋ฌํ (ํ๊ฒฉ์ ์ธ) ๋ณ๊ฒฝ( class Component<P, S, D>
์ ์๋ฏธํจ)์ด ๋ฆฌ๋ทฐ์ด์ ์ํด ์น์ธ๋ ์ ์๋ค๊ณ ์๊ฐํ์ง ์์ต๋๋ค.
@johnnyreilly @bbenezech @pzavolinsky ์ฌ๋ฌ๋ถ์ ๊ทธ๊ฒ์ ๋ํด ์๊ฒฌ์ด ์์ผ์ญ๋๊น?
@r00ger๊ฐ ๋์ํ์ต๋๋ค. ์ ์๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ ๋๋ฌด ๋ฐฉํด๊ฐ ๋ฉ๋๋ค.
Partial
์ฌ์ฉ์ ๊ณ ๋ คํ ์ฌ๋์ด ์์ต๋๊น?
์์์ ๊ฐ์ด:
interface ComponentClass<P> {
- defaultProps?: P;
+ defaultProps?: Partial<P>;
}
์์ Partial
ํญ๋ชฉ์ ์ ๊ฒฝ์ฐ์ง ๋ง์ญ์์ค.
๋ถ๋ถ์ ๋ถ๋ถ propTypes ๋ฌธ์ ๋ฅผ ์ ์ธํ๋ ๋ฐฉ๋ฒ๋ง ํด๊ฒฐํฉ๋๋ค. render
lastName
๋ ์ฌ์ ํ string | undefined
์ ํ์
๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์๋์ ๊ฐ์ด as
๋๋ !
๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์์ด์ ์บ์คํ
ํด์ผ ํฉ๋๋ค. ์๋ํ์ง๋ง ์ด์์ ์ด์ง๋ ์์ต๋๋ค.
interface IUser {
firstName: string
lastName?: string
}
export class User extends React.Component<IUser, {}> {
public static defaultProps: Partial<IUser> = {
lastName: 'None',
}
public render () {
const { firstName, lastName } = this.props
// error
lastName.toUpperCase()
return (
<div>{firstName} {lastName}</div>
)
}
}
TS๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ์ต๋๋ค. ๋ด๊ฐ ๋ญ๊ฐ๋ฅผ ๋์น๊ณ ์์ต๋๊น?
์ ํ ๋ฐ defaultProps์ ๋ํ ์ข์ ์๋ฃจ์ ์ด ์๋ ์ฌ๋์ด ์๋ค๋ฉด ์ ๋ ๋ชจ๋ ๊ท์ ๋๋ค. ํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ํ๊ณ ์์ต๋๋ค.
interface Props {
firstName: string;
lastName?: string;
}
interface DefaultProps {
lastName: string;
}
type PropsWithDefaults = Props & DefaultProps;
export class User extends React.Component<Props> {
public static defaultProps: DefaultProps = {
lastName: 'None',
}
public render () {
const { firstName, lastName } = this.props as PropsWithDefaults;
return (
<div>{firstName} {lastName}</div>
)
}
}
+1
๋๋ ํ์ฌ ์ด ๋ฌธ์ ์ ์ธ์ฐ๊ณ ์๋ค.
+1
+1
์ธ ๋ฒ์งธ ์ ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ๋ ๊ฒ ์ธ์๋ ๊ธฐ๋ณธ props์ ๋ํด props๋ฅผ ๊ตฌ๋ณํ๋ ๊ธฐ๋ฅ์ด ํ์ํฉ๋๋ค. ๋คํ์ค๋ฝ๊ฒ๋ TS 2.4๋ถํฐ ์ด๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค! https://github.com/Microsoft/TypeScript/issues/12215#issuecomment -319495340 ์ฐธ์กฐ
์ธ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ๋ IMHO๋ ํฐ ๋ฌธ์ ๊ฐ ์๋๋๋ค. ๋ํ Flow ํ๋ ์ด๋ฅผ ์๊ณ ์์๊ณ ์ต๊ทผ์ ์ด๋ฅผ ๋ณ๊ฒฝํ์ต๋๋ค. ์ด๋ฌํ ์ข ๋ฅ์ ์ผ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ๊ฒ์ ์ ํ ๊ฒ์ฌ๊ธฐ์ ์ฑ ์์ด์ด์ผ ํฉ๋๋ค.
์คํดํ์ง ๋ง์ธ์. ์ ๋ Typescript๋ฅผ ์ฌ๋ํ์ง๋ง Flow 0.53๋ถํฐ React ๊ฐ๋ฐ์ ๋ ์ฐ์ํ๋ค๊ณ ๋งํด์ผ ํฉ๋๋ค. https://medium.com/flow-type/even-better-support-for-react-in-flow- 25b0a3485627
@Hotell Flow์๋ React.Component
์ ๋ํ ์ธ ๊ฐ์ง ์ ํ ๋งค๊ฐ๋ณ์๊ฐ ์์ต๋๋ค. - Flow์ ๋งํฌ๋ Medium ๊ธฐ์ฌ์ ๋ฐ๋ผ ํ์ ํด๋์ค ์ฃผ์์์ ํด๋์ค ์ ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ ์ถํ ์ ์์ต๋๋ค. - ๊น๋ํ ์ธ์ด ์์ค ๊ธฐ๋ฅ TS๋ ์ง์ํ์ง ์์ง๋ง ์ ํ์ ์ง์ํ์ง ์์ต๋๋ค. -์ ์ธ ๊ณ ๋ ค AFAIK.
@aldendaniels
Flow์๋ React.Component์ ๋ํ ์ธ ๊ฐ์ง ์ ํ ๋งค๊ฐ๋ณ์๊ฐ ์์ต๋๋ค.
์๋์, 0.53 ์ด์ ์๋ ๊ทธ๋ฌ๊ณ ๋ ์ด์์ ์๋๋๋ค.
@ํธํ ์, ๋ฌผ๋ก ์ ๋๋ค! ์์ ํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
AFAIK๋ TS์์ ๊ธฐ๋ณธ ์ํ์ ์ ํ์ ์ ์ถํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ์ธ ๊ฐ์ง ์ ํ์ ๋งค๊ฐ๋ณ์ ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด TypeScript ํ์ ์ ์คํธ๋ฆผ ๋ณ๊ฒฝ ์ฌํญ์ ์ฐจ๋จํ์ง ์๊ณ ์ฌ๋ฐ๋ฅธ ์ ๋ ฅ์ ์ป์ ์ ์์ต๋๋ค.
typeof MyComponent.defaultProps
๋ฅผ type ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌํ์ง ์๊ณ ์ ์ถ๋ ์ ํ์ ์ ์ ์์ฑ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์์ต๋๊น?
์ด ์ฃผ์ ์ ๋ํ ์์์ด ์์ต๋๊น? ๋๊ตฐ๊ฐ ์ธ ๋ฒ์งธ ์ ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ๊ณ https://github.com/Microsoft/TypeScript/issues/12215#issuecomment -319495340์ ์ฌ์ฉํ๊ธฐ ์ํด PR์ ํฉ๋๊น?
์ฐฌ์ฑ ๋ฌธ์ : ๋์ผํ ๋ฌธ์
+1
๋๋ ๋ํ ์ด๊ฒ์ ๋ถ๋ช์ณค๊ณ static defaultProps
์ฌ์ฉ์ ์์ ํ๊ณ ๋์ ๋์ฐ๋ฏธ HOC๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก (์ด ๋ฌธ์ ๊ฐ ์ ๋๋ก ์์ ๋ ๋๊น์ง) ์ ํํ์ต๋๋ค.
ํ์ผ ๊ตฌ์ฑ ์์/helpers/withDefaults.tsx :
import * as React from 'react'
export interface ComponentDefaulter<DP> {
<P extends {[key in keyof DP]?: any}>(Component: React.ComponentType<P>): React.ComponentType<
Omit<P, keyof DP> & // Mandate all properties in P and not in DP
Partial<Pick<P, keyof DP>> // Accept all properties from P that are in DP, but use type from P
>
}
export default function withDefaults<DP>(defaultProps: DP): ComponentDefaulter<DP> {
return Component => props => <Component {...defaultProps} {...props}/>
}
์ด์ ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ผ ๊ตฌ์ฑ ์์/Button.tsx :
import * as React from 'react'
import withDefaults from './helpers/withDefaults'
export interface ButtonProps {
label: string
onPress: () => any
}
export const defaultProps = {
onPress: () => undefined
}
class Button extends React.Component<ButtonProps> {
// ...
}
export default withDefaults(defaultProps)(Button)
์ธ ๊ฐ์ง ์ ์ฌ์ ์ธ ๋จ์ (๋ด๊ฐ ์๊ฐํ ์ ์๋):
props
์์ฑ์ ์ถ๋ก ์ ์์กดํ ์ ์์ต๋๋ค.defaultProps
์ ์ ํ์ ๋ํ ์์์ ๊ฒ์ฌ๋ ์์ง๋ง export const defaultProps: Partial<ButtonProps> = {...}
๋ฅผ ์ง์ ํ์ฌ ํด๊ฒฐํ ์ ์์ต๋๋ค.@vsaarinen ์ ๋ฐ๋ฅด๋ฉด props: Props & DefaultProps
๊ธฐ๋ณธ ํด๋์ค๋ฅผ ์์ฑํ๋ฏ๋ก ๊ธฐ๋ณธ ํด๋์ค๋ฅผ ํ์ฅํ๋ ์ ์ฒด ํด๋์ค๋ this.props as PropsWithDefaults
$ ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ this.props
๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด์ ๊ฐ์ด:
import * as React from 'react'
export class Component<P = {}, S = {}, DP = {}> extends React.Component<P, S> {
props: Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>
}
export interface Props {
firstName: string
lastName?: string
}
export interface DefaultProps {
lastName: string
}
export class User extends Component<Props, any, DefaultProps> {
render() {
const { firstName, lastName } = this.props
// no error
return (
<div>{firstName} {lastName.toUpperCase()}</div>
)
}
}
์ค์ ๋ก @qiu8310 ์ ์์ ํ ์๋ํ์ง ์์์ง๋ง ์ฌ์ ํ ๊ธฐ๋ณธ ์ํ์ด ์ ํ ์ฌํญ์ด ์๋๋ผ๊ณ ๋น๋ช ์ ์ง๋ฅด๋ ํธ์ถ ์ฌ์ดํธ์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. ์ฝ๊ฐ์ ์กฐ์ ์ผ๋ก ์๋ํ๋๋ก ํ์ต๋๋ค.
import * as React from 'react'
export class Component<P = {}, S = {}, DP = {}> extends React.Component<P, S> {
// Cast the props as something where readonly fields are non optional
props = this.props as Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>
}
export interface Props {
firstName: string
lastName?: string
}
export interface DefaultProps {
lastName: string
}
export class User extends Component<Props, any, DefaultProps> {
render() {
const { firstName, lastName } = this.props
// no error
return (
<div>{firstName} {lastName.toUpperCase()}</div>
)
}
}
๋๋ ์ 3์ ์ ๋ค๋ฆญ์ ๊ฐ์ง๊ณ ๋์๊ณ ๊ฒฐ๊ตญ @qiu8310 ์ ์ ์๊ณผ ๋น์ทํ ๊ฒ์ ๊ฐ๊ฒ ๋์์ต๋๋ค:
// ComponentWithDefaultProps.ts
import * as React from "react";
export declare class ComponentWithDefaultProps<P, S, DP extends Partial<P>> extends React.Component<P & DP, S> {}
type redirected<P, S, DP> = ComponentWithDefaultProps<P, S, DP>;
const redirected: typeof ComponentWithDefaultProps = React.Component as any;
export const Component = redirected;
// User.ts
import { Component } from "ComponentWithDefaultProps";
export interface Props {
firstName: string
lastName?: string
}
export interface DefaultProps {
lastName: string
}
export class User extends Component<Props, {}, DefaultProps> {
public render() {
const { firstName, lastName } = this.props;
return <div>{firstName} {lastName.toUpperCase()}</div>;
}
}
๊ทธ๋ฌ๋ ์ด ๋ ๊ฐ์ง ์ ๊ทผ ๋ฐฉ์(๋์ ์์ ์ ๊ทผ ๋ฐฉ์) ๋ชจ๋ ๋ ํฐ ๋ฌธ์ ๋ฅผ ์ผ์ผํต๋๋ค. ๋ด ์์๋ ๋ค์๊ณผ ๊ฐ์ ์ ํ์ ์์ฑ๋ ๊ตฌ์ฑ ์์๊ฐ ์์ต๋๋ค.
User: React.ComponentClass<P & DP>
User["props"]: Readonly<{ children?: React.ReactNode }> & Readonly<P & DP>
๋ถ๋ช
ํ User
์ ์ธํฐํ์ด์ค๊ฐ ์๋ชป๋์์ต๋๋ค. React.ComponentClass<P & DP>
๋ lastName
๋ ํ์ํจ์ ์๋ฏธํ๋ฏ๋ก
<User firstName="" />;
// ~~~~~~~~~~~~ Property 'lastName' is missing...
@qiu8310 ์ ์์ ์ ํ์ ๋ค์๊ณผ ๊ฐ์ด ๋ค๋ฆ ๋๋ค.
User: React.ComponentClass<P>
User["props"]: Readonly<{ children?: React.ReactNode }> & Readonly<P> & Readonly<DP>
๊ทธ๋ฌ๋ tsc
์ JSX ๊ฒ์ฌ๋ props
' type ์ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ๋์ผํ JSX ์กฐ๊ฐ์ด ๋์ผํ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
<User firstName="John" />;
// ~~~~~~~~~~~~~~~~ Property 'lastName' is missing...
์ฌ๋ฏธ์๋ ์ ์ <User firstName="John" />
๊ฐ React.createElement(User, {firstName: "John"})
๋ก ๋ฐ๋๊ณ ์๋ค๋ ๊ฒ์
๋๋ค. ์ด๋ ์ ํจํ TypeScript์
๋๋ค. ์ด ๊ฒฝ์ฐ ์ ํ ๊ฒ์ฌ๋ ComponentClass
์ฒซ ๋ฒ์งธ ์ ํ ๋งค๊ฐ๋ณ์์ ์์กดํ๋ฏ๋ก
<User firstName="Jonh" />; // doesn't work, but
React.createElement(User, { firstName: "John" }); // works
๋ณด์๋ค์ํผ ์ 3์ ์ ๋ค๋ฆญ์ด ์๋๋ผ๋ ์ฌ๋ฐ๋ฅธ ์ธํฐํ์ด์ค๋ก ๊ตฌ์ฑ ์์๋ฅผ ๋ด๋ณด๋ด๋ ค๋ฉด ์ฌ์ ํ ๋ ๋ค๋ฅธ ํธ๋ฆญ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
export const User = class extends Component<Props, {}, DefaultProps> {
// ...
} as React.ComponentClass<Props>;
<User firstName="Jonh" />; // works
๋ฐ๋ผ์ ์ 3์ ์ ๋ค๋ฆญ์ ๊ฐ๋ ๊ฒ์ ๋ณ๋ก ์๋ฏธ๊ฐ ์์ต๋๋ค.
React
์ ์ ์์ ๋ณํฉํ ์ ์๋ ์ข์ ์๋ฃจ์
์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ง๊ธ์ ComponentWithDefaultProps
๋ฅผ ์ฌ์ฉํ๊ณ ๋ด๋ณด๋ธ ๊ตฌ์ฑ ์์์ ์ ํ์ ์ฃผ์ฅํ๋ ๊ฒ์ ๊ณ ์ํฉ๋๋ค.
export interface DefaultProps {
lastName: string;
}
export interface Props extends Partial<DefaultProps> {
firstName: string;
}
export type PropsWithDefault = Props & DefaultProps;
export const User: as React.ComponentClass<Props> =
class extends React.Component<PropsWithDefault> {
render() {
// no error
return <div>
{this.props.firstName}
{this.props.lastName.toUpperCase()}
</div>;
}
};
// Note, we've assigned `React.Component<PropsWithDefault>` to `React.ComponentClass<Props>`
๊ทธ ์ธ์๋ ๊ตฌ์ฑ ์์์ ๋ฉ์๋์์ this.props
์ ํ์ ๋ชจ๋ ์ฌ์ฉ์ ์ฃผ์ฅํ ์ ์์ต๋๋ค(์: const { lastName } = this.props as Props & DefaultProps
๋๋ this.props.lastName!.toLowerCase()
์ ๋๋ํ ์ฌ์ฉ).
์ด ํ ๋ก ์ ๋ํ ๋ช ๊ฐ์ง ์๋ฅผ ์ฐพ์์ต๋๋ค - https://github.com/gcanti/typelevel-ts#objectdiff
HOC ์ ๊ทผ ๋ฐฉ์(์ ๋ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ํธํฉ๋๋ค)์ด๋ผ๊ณ ํ๋ @rifler ๊ฐ ์ ์ ๋์ ์ฌ๊ธฐ์ ์์์ต๋๋ค . ์ฐ๋ฆฌ๋ ๋ฐํ์ ์ค๋ฒํค๋๋ฅผ ์ถ๊ฐํ์ง ์๋ ์๋ฃจ์ ์ ์ ์ํ๋ ค๊ณ ํฉ๋๋ค.
์ค ์ข์
๋น์ ์ด ํด๊ฒฐ์ฑ
์ ์ฐพ์ ์ ์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค
์ด๋ค ์ง์ ?
๋ค์์ @r00ger ๊ฐ ์ธ๊ธํ ๊ธฐ์ ์ ๋ณํ์ ๋๋ค.
interface IUser {
name: string;
}
const User = class extends React.Component<IUser> {
public static defaultProps: IUser = {name: "Foo"}
public render() {
return <div>{this.props.name}</div>;
}
} as React.ComponentClass<Partial<IUser>>;
React.createElement(User, {}); // no error, will output "<div>Foo</div>"
์์ ์ค๋ํซ์ ์ฌ์ฉํ๋ฉด ์๋ํ์ง๋ง ์ฌ์ฉ์๋ ์ต๋ช ํด๋์ค๊ฐ ๋๋ฏ๋ก ์ ์ ์์ฑ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ํดํค ์๋ฃจ์ ์ ๋ค์๊ณผ ๊ฐ์ด ํด๋์ค ์ด๋ฆ์ ์๋์ํ๋ ๊ฒ์ ๋๋ค.
// tslint:disable-next-line:no-shadowed-variable
const User = class User extends React.Component<IUser>
์ด์ ํด๋์ค ๋ด์์ ๊ฐ์ธ์ฉ ์ ์ ํ๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ณต๊ฐ ํต๊ณ๋ ์ฌ์ ํ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ํ tslint๋ฅผ ๋ฌต์ ์ฒ๋ฆฌํด์ผ ํ ํ์์ฑ์ ์ฃผ๋ชฉํ์ญ์์ค.
TS 2.8๋ถํฐ Exclude
์ ํ์ด ๊ณต์์ ์ผ๋ก ์ง์๋๋ค๋ ์ ์ ์ธ๊ธํ ๊ฐ์น๊ฐ ์๋ค๊ณ ์๊ฐํ์ต๋๋ค.
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
https://github.com/Microsoft/TypeScript/pull/21847์ ์ฐธ์กฐํ์ธ์.
๋ฐ๋ผ์ React.createElement()
๊ฐ Props
๋์ ๋ค์์ ์๊ตฌํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
Omit<Props, keyof DefaultProps>
์ ์ผํ ๋ฌธ์ ๋ React ์ ์ธ์ DefaultProps
์ ํ์ด ์๋ค๋ ๊ฒ์
๋๋ค. ์ด๋ฅผ ์ํด์๋ ์ธ ๋ฒ์งธ ์ ํ ๋งค๊ฐ๋ณ์๊ฐ ํ์ํ๊ฑฐ๋ ์ ์ ๋ฉค๋ฒ ์ ํ์ ์ธ์ด ๊ธฐ๋ฅ์ผ๋ก ์ ์ถํ๋ ๊ธฐ๋ฅ์ด ํ์ํฉ๋๋ค.
๊ทธ ๋์ ์ฐ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ ์์ ์ ์งํํ์ต๋๋ค.
/**
* The Create type allow components to implement a strongly thed create() function
* that alows the caller to omit props with defaults even though the component expects
* all props to be populated. The TypeScript React typings do not natively support these.
*/
export type Create<C extends BaseComponent<any, any>, D extends {} = {}> = (
props?: typeHelpers.ObjectDiff<C['props'], D> & React.ClassAttributes<C>,
...children: React.ReactNode[]
) => React.ComponentElement<any, any>;
export interface DomPropsType {
domProps?: domProps.DomProps;
}
export class BaseComponent<P, S = {}> extends React.Component<P & DomPropsType, S> {
static create(props?: object, ...children: React.ReactNode[]) {
return React.createElement(this, props, ...children);
}
constructor(props: P & DomPropsType, context?: any) {
...
}
๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ๊ตฌ์ฑ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
export class InsertObjectMenu extends BaseComponent<Props, State> {
static create: Create<InsertObjectMenu, typeof InsertObjectMenu.defaultProps>;
static defaultProps = {
promptForImageUpload: true,
};
...
}
๋ง์ง๋ง์ผ๋ก create
์์ฑ์ด ๋ชจ๋ ๊ตฌ์ฑ ์์์ ์ ์ธ๋๋๋ก ํ๋ ๋ฆฐํธ ๊ท์น์ด ์์ต๋๋ค. ์ฐ๋ฆฌ๋ JSX๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฏ๋ก ๋ค์์ ์ฌ์ฉํฉ๋๋ค.
InsertObjectMenu.create({...})
React.createElement()
๋์ .
์ฐ๋ฆฌ๋ 1๋ ๊ฐ๊น์ด ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค์์ ์ด ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํด ์์ผ๋ฉฐ ์ฑ๊ณต์ ์ผ๋ก ์ํํ์ง๋ง JSX๋ฅผ ์ฑํํ๊ณ ์ถ๊ณ ์ด๊ฒ์ด ์ฐ๋ฆฌ๋ฅผ ๊ฐ๋ก๋ง๊ณ ์์ต๋๋ค.
์ด "๊ฐ๋จํ ๋ฌธ์ "์ ๋๋ฌด ๋ง์ ์๊ฐ์ ํฌ์ํ์ต๋๋ค. ์ฌ๊ธฐ https://medium.com/@martin_hotell/ultimate -react-component-patterns-with-typescript-2-8-82990c516935 ๐
interface Component<P = {}, S = {}, DP extends Partial<P>=P> extends ComponentLifecycle<P, S> { }
class Component<P, S, DP extends Partial<P> = P> {
constructor(props: P & DP, context?: any);
// We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
// See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
// Also, the ` | S` allows intellisense to not be dumbisense
setState<K extends keyof S>(
state: ((prevState: Readonly<S>, props: P) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
callback?: () => void
): void;
forceUpdate(callBack?: () => void): void;
render(): ReactNode;
// React.Props<T> is now deprecated, which means that the `children`
// property is not available on `P` by default, even though you can
// always pass children as variadic arguments to `createElement`.
// In the future, if we can define its call signature conditionally
// on the existence of `children` in `P`, then we should remove this.
private __externalProps: Readonly<{ children?: ReactNode }> & Readonly<P>;
props: Readonly<{ children?: ReactNode }> & Readonly<P> & DP;
state: Readonly<S>;
context: any;
refs: {
[key: string]: ReactInstance
};
}
class PureComponent<P = {}, S = {}, DP extends Partial<P>=P> extends Component<P, S, P> { }
interface ElementAttributesProperty { __externalProps: {}; }
๋ง์ง๋ง ์ค์ ์ ๋ณด์ธ์.
์ด ๋ณ๊ฒฝ ์ฌํญ์ผ๋ก ์ฐ๋ฆฌ๋
interface Props {
a: string
b?: string
c?: string
}
class Comp extends React.Component<Props, {}, typeof Comp.defaultProps> {
static defaultProps = {
b: ''
}
render() {
const {a, b, c} = this.props
let res = a.concat(b) // ok
let res1 = a.concat(c) //fail
return null
}
}
const res1= <Comp a=''/> // ok
const res3 = <Comp /> // fail
static defaultProps
( typeof Comp.defaultProps
๋ฅผ ์๋ตํ๋ ค๋ฉด ts ๊ฒ์ฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํด์ผ ํจ)๋ฅผ ์ฌ์ฉํ ๋ ์ป์ ์ ์๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๋ฌด์์
๋๊น?
๋ค๋ฅธ ์ต์
์ ์ด๋ฏธ ์ธ๊ธ๋์์ต๋๋ค - HOC, ์ ํ ์บ์คํธ.
๋ค์์ https://medium.com/@martin_hotell/ultimate -react-component-patterns-with-typescript-2-8-82990c516935์ ์์ด๋์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ด (๋งค์ฐ ๋ชป์๊ธด) ์๋์ ๋๋ค.
type ExtractProps<T> = T extends React.ComponentType<infer Q> ? Q : never;
type ExtractDefaultProps<T> = T extends { defaultProps?: infer Q } ? Q : never;
type RequiredProps<P, DP> = Pick<P, Exclude<keyof P, keyof DP>>;
type RequiredAndPartialDefaultProps<RP, DP> = Required<RP> & Partial<DP>;
type ComponentTypeWithDefaultProps<T> =
React.ComponentType<
RequiredAndPartialDefaultProps<
RequiredProps<ExtractProps<T>, ExtractDefaultProps<T>>,
ExtractDefaultProps<T>
>
>;
function withDefaultProps<T extends React.ComponentType<any>>(Comp: T) {
return Comp as ComponentTypeWithDefaultProps<T>;
}
interface IProps {
required: number;
defaulted: number;
}
class Foo extends React.Component<IProps> {
public static defaultProps = {
defaulted: 0,
};
}
// Whichever way you prefer... The former does not require a function call
const FooWithDefaultProps = Foo as ComponentTypeWithDefaultProps<typeof Foo>;
const FooWithDefaultProps = withDefaultProps(Foo);
const f1 = <FooWithDefaultProps />; // error: missing 'required' prop
const f2 = <FooWithDefaultProps defaulted={0} />; // error: missing 'required' prop
const f3 = <FooWithDefaultProps required={0} />; // ok
const f4 = <FooWithDefaultProps required={0} defaulted={0} />; // ok
@decademoon , ์ด ์๋ฃจ์
์ @types/react
์์ ์ฌ์ฉํ ์ ์์ ๊ฒ ๊ฐ์ต๋๋ค. ๋ด ๋ง์, ์ผ๋ฐ์ ์ธ React.ComponentType
๋ฅผ ๊ทํ์ ์๋ฃจ์
์ผ๋ก ๋์ฒดํ๋ ๊ฒฝ์ฐ์
๋๋ค.
๊ทธ๋ ๋ค๋ฉด PR์ ์์ฑํ ์ ์์ต๋๊น?
@decademoon ๊ทํ์ ์ ์๋ ๊ธฐ๋ณธ์ด ์๋ ์ํ์ด ์ค์ ๋ก ์ ํ์ ํ๋๋ฅผ ํฌํจํ๋ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ์ง ์์ต๋๋ค.
interface IProps {
required: number;
notRequired?: () => void;
defaulted: number;
}
class Foo extends React.Component<IProps> {
public static defaultProps = {
defaulted: 0,
};
}
๋ด ๊ฒฝ์ฐ์๋ "RP"๋ฅผ "ํ์"๋ก ๋ํํ์ง ์๋๋ก RequiredAndPartialDefaultProps ์ ํ์ ๋ณ๊ฒฝํ์ฌ ์๋ํ๋๋ก ํ์ต๋๋ค.
type RequiredAndPartialDefaultProps<RP, DP> = RP & Partial<DP>;
์ฌ์ ํ ์ ์ ํ ์๋ฃจ์ ์ด ์๊ฑฐ๋ NPM์์ ์๋ํ๋ HOC๊ฐ ์๋ค๋ ์ฌ์ค์ ๋๋์ต๋๋ค. ๋ด๊ฐ ๋์น ๊ฒ์ด ์๋ค๋ฉด.
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ. ์ด ์ค๋ ๋๋ฅผ ์ฌ์ ํ ์ฝ๊ณ ์๋ค๋ฉด @JoshuaToenyes ๊ฐ ๊ฐ์ฅ ์๋ฏธ ์๊ณ ๋์์ด ๋๋ ์ค๋ช ์ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ด๊ฒ์ ํ์คํ ๋ฌธ์ ๊ฐ ์๋๋ฏ๋ก ๊ด๋ จ์ด ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ์ ํ ์ด์ค์ ์ ์ฌ์ฉํ์ญ์์ค.
@toiletpatrol ์ค์ ๋ก @decademoon ์ ์๋ฃจ์ (์ฝ๊ฐ์ ์์ ํฌํจ)์ ๊ธฐ๋ณธ ์ํ์ ์๋์ผ๋ก ์ ์ฒ๋ฆฌํฉ๋๋ค. ๋ชจ๋ ์ฌ๋์๊ฒ ๊ธฐ๋ฅ ํ์ค์ ์ ๊ณตํ๊ธฐ ์ํด React์ DT ์ ์์ ํ์คํ ๋ณํฉ๋ ์ ์์ต๋๋ค.
@toiletpatrol @RobRendell ๋ณด์ จ๋์ https://github.com/Microsoft/TypeScript/issues/23812?
@vkrol ๋๋ ๊ทธ๊ฒ์ ๋ณด์์ง๋ง, ์๋ก์ด ๊ธฐ๋ฅ์ ๋ฆด๋ฆฌ์ค๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ์ง๊ธ ๋น์ฅ ๋ด ์ฝ๋๋ฒ ์ด์ค์ Decademoon์ ๊ตฌํ์ ์ญ์ ํ ์ ์์ต๋๋ค.
๊น๋ค๋ก์ด ๊ฒฝ์ฐ์ ํ์ฌ ์ฌ์ฉํ๊ณ ์๋ ๋ ๋ค๋ฅธ ํด๊ฒฐ ๋ฐฉ๋ฒ:
const restWithDefaults = { ...Component.defaultProps, ...rest };
return <Component {...restWithDefaults} />;
์๋ฌด ๋ฌธ์ ๊ฐ ์๋ ๊ฒ ๊ฐ์ผ๋ฏ๋ก ๋๋ฝ์ง๋ง ๊ฐ๋จํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ๊ธฐ์ ๋จ๊ฒจ ๋ก๋๋ค.
TS 3.2 ๋ฐ react 16.7 ํ์ดํ์ด ์ด ๋ฌธ์ ๋ฅผ ์์ ํ๊ณ ์์ต๋๋ค. ๋ซ์ ์ ์์ต๋๊น?
@Hotell ๊ฒฐ๊ตญ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผํฉ๋๊น? ๋๋ ์์ง๋ ์ด๊ฒ์ ์ ๋๋ก ์๋์ํค์ง ๋ชปํ๋ค.
๋ค๋ฅธ ์ฌ๋๋ค์ ์๊ฐ์ ์ ์ฝํ๊ธฐ ์ํด ๋ค์์ Typescript 3์ ๋ฆด๋ฆฌ์ค ์ ๋ณด์ ๋ํ ๋งํฌ์
๋๋ค.
JSX์์ defaultProps ์ง์
@cbergmiller TypeScript 3.1์ ๋ฆด๋ฆฌ์ค ๋ ธํธ์ ๋๋ค ๐
React.FunctionComponent
์ ์ฌ์ ํ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
@denieler React.FunctionComponent
defaultProps
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ์ง ์์ต๋๋ค. ์์ฐ์ค๋ฝ์ง ์์ต๋๋ค. ๊ธฐ๋ณธ ํจ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
interface HelloProps {
name?: string;
surname?: string;
}
const HelloComponent: React.FunctionComponent<HelloProps> = ({
name = 'John',
surname = 'Smith',
}) => {
return <div>Hello, {name} {surname}!</div>
};
@mgol props ๋ฅผ ๊ตฌ์กฐํํ์ง ์์ผ๋ ค๋ฉด ๊ธฐ๋ณธ ํจ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ด๋ป๊ฒ ์ ์ํ ๊น์?
๋ค์๊ณผ ๊ฐ์ด "๊ธฐ๋ณธ๊ฐ" ์์ฑ๋ง ๊ตฌ์กฐํํ๋ ๊ฒ๋ง ์๊ฐํ ์ ์์ต๋๋ค.
interface HelloProps {
name?: string;
surname?: string;
}
const HelloComponent: React.FunctionComponent<HelloProps> = ({
name = 'John',
surname = 'Smith',
...props
}) => {
return <div>Hello, {name} {surname}! You are {props.age} years old.</div>
};
ํ์ง๋ง ์ผ๋ถ ์ํ๋ง ์ถ์ถํ๋ ๊ฒ์ ๋ถ๋๋ฌ์ด ์ผ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
@glecetre ๋ค์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
HelloComponent.defaultProps = {
name: 'John',
surname: 'Smith'
}
@Glinkis ๋ https://github.com/reactjs/rfcs/pull/107/files#diff -20b9b769068a185d90c23b58a2095a9dR184๋ฅผ ์ฐธ๊ณ ํ์ธ์.
@glecetre ์ ๋ชจ๋ ์ํ์ ๋ถํดํ๊ณ ์ถ์ง ์์ต๋๊น? defaultProps
๋ฅผ ์ ์ํ๋ ๊ฒ๋ณด๋ค ๊ฐ๋จํ๊ณ ์
๋ ฅํ๊ธฐ ์ฝ์ต๋๋ค. ํด๋์ค ๊ธฐ๋ฐ ๊ตฌ์ฑ ์์์ props ์ ํ์ ํ์ํ props๊ฐ defaultProps
์ ํญ๋ชฉ์ด ์์ผ๋ฉด ๋ ์ด์ ํ์ํ์ง ์์ ์ ์์ผ๋ฏ๋ก ์ธ๋ถ์์ ์ฌ์ฉํ๊ธฐ ์ํด ๋ด๋ณด๋ด๋ ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค. defaultProps
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ์ฝ๊ฐ ๋ง๋ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง ๋งค๊ฐ๋ณ์ ๊ตฌ์กฐํ์์๋ ๋ชจ๋ JavaScript์
๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ ํ ๋ฐ defaultProps์ ๋ํ ์ข์ ์๋ฃจ์ ์ด ์๋ ์ฌ๋์ด ์๋ค๋ฉด ์ ๋ ๋ชจ๋ ๊ท์ ๋๋ค. ํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ํ๊ณ ์์ต๋๋ค.