Definitelytyped: 일뢀 κΈ°μ‘΄ μ—°κ²° 호좜이 5.0.16에 μ˜ν•΄ 쀑단됨

에 λ§Œλ“  2018λ…„ 04μ›” 11일  Β·  3μ½”λ©˜νŠΈ  Β·  좜처: DefinitelyTyped/DefinitelyTyped

  • [x] @types/xxxx νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•΄ λ³΄μ•˜λŠ”λ° λ¬Έμ œκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.
  • [x] μ•ˆμ •μ μΈ μ΅œμ‹  λ²„μ „μ˜ tscλ₯Ό μ‚¬μš©ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. https://www.npmjs.com/package/typescript
  • [x] StackOverflow 에 μ ν•©ν•˜μ§€ μ•Šμ€ 질문이 μžˆμŠ΅λ‹ˆλ‹€. (μ μ ˆν•œ 질문이 있으면 κ±°κΈ°μ—μ„œ μ§ˆλ¬Έν•˜μ‹­μ‹œμ˜€.)
  • [x] [λ©˜μ…˜](https://github.com/blog/821-mention-somebody-they-re-notified) μž‘μ„±μž( Definitions by: in index.d.ts ) λŒ€λ‹΅ν•˜λ‹€.

    • μž‘μ„±μž: @Pajn @tkqubo @thasner @kenzierocks @clayne11 @tansongyang @NicholasBoll @mDibyo @pdeva

문제

μ—¬λ³΄μ„Έμš”! 방금 5.0.16 μ—…λ°μ΄νŠΈν–ˆλŠ”λ° κΈ°μ‘΄ ν”„λ‘œμ νŠΈμ—μ„œ μ΄λŸ¬ν•œ μƒˆ μž…λ ₯을 μ‚¬μš©ν•˜λŠ” 데 λ¬Έμ œκ°€ μ¦‰μ‹œ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μŒμ€ λͺ‡ 가지 κ΄€λ ¨ 버전 λ²ˆν˜Έμž…λ‹ˆλ‹€.

@types/react-redux: ^5.0.16,
typescript: ^2.8.x,
react-redux: ^5.0.5,

일반적인 μ»¨ν…Œμ΄λ„ˆλŠ” μ»΄νŒŒμΌλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

λ‹€μŒμ€ μ»΄νŒŒμΌν•΄μ•Ό ν•œλ‹€κ³  μƒκ°ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€( 5.0.16 이전에 μ»΄νŒŒμΌλ˜μ—ˆκ³  버전을 λ‹€μ‹œ 5.0.15 μ„€μ •ν•˜λ©΄ λ‹€μ‹œ 컴파일됨).

// components/exampleComponent.ts
import React = require('react')
import { IState, IDateRange, ReportType } from '../../types/index'

export interface IExampleProps {
  label?: string
  dateRange: IDateRange
  onDateRangeChange: (value: IDateRange) => void
}

export class ExampleComponent extends React.Component<IExampleProps> {
  // implementation
}
// containers/exampleComponent.ts
import { connect } from 'react-redux'
import { IState, IDateRange, ReportType } from '../../types/index'

interface IPropsFromState {
  dateRange: IDateRange
}

interface IPropsFromParent {
  reportType: ReportType
}

const mapStateToProps = (state: IState, props: IPropsFromParent): IPropsFromState => {
  const config = state.reporting.reportConfigs[props.reportType]

  return {
    dateRange: config ? config.dateRange : null,
  }
}

const connector = connect<IPropsFromState, null, IPropsFromParent>(
  mapStateToProps,
)

export const ExampleContainer = connector(ExampleComponent) // <-- this does not compile

ν‰μ†ŒλΌλ©΄ 이런 용기λ₯Ό 계속 μ‚¬μš©ν•˜κ² μ§€λ§Œ,

<ExampleContainer reportType={ReportType.USERS_CREATED} />

κ·ΈλŸ¬λ‚˜ μ‚¬μš©ν•˜μ—¬μ΄ μ½”λ“œμ™€ μ΅œμ‹  @types/react-redux μ—μ„œ 5.0.16 μ—μ„œμ™€ 타이프 라이터 2.8.1 λ‚΄κ°€ λŒ€μ‹  λ‹€μŒκ³Ό 같은 였λ₯˜λ₯Ό :

[ts]
Argument of type 'typeof ExampleComponent' is not assignable to parameter of type 'ComponentType<IPropsFromState & DispatchProp<any> & IPropsFromParent>'.
  Type 'typeof ExampleComponent' is not assignable to type 'StatelessComponent<IPropsFromState & DispatchProp<any> & IPropsFromParent>'.
    Type 'typeof ExampleComponent' provides no match for the signature '(props: IPropsFromState & DispatchProp<any> & IPropsFromParent & { children?: ReactNode; }, context?: any): ReactElement<any>'.

5.0.16 λ„μž…λœ λ³€κ²½ 사항을 κ²€μ‚¬ν•˜λ©΄ connector μœ ν˜•μ΄ InferableComponentEnhancerWithProps<IPropsFromState & IPropsFromParent, IPropsFromParent> μ΄λ―€λ‘œ μœ ν˜• μ‹œμŠ€ν…œμ΄ 잘λͺ»λœ κ²ƒμ²˜λŸΌ λŠκ»΄μ§‘λ‹ˆλ‹€. μ΄λŠ” 첫 번째 μΈμˆ˜κ°€ IPropsFromState & IPropsFromParent μœ ν˜•μΌ κ²ƒμœΌλ‘œ μ˜ˆμƒν•œλ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€ reportType 속성이 μ—†μŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ λ‹€μŒκ³Ό 같이 μž‘μ„±ν•©λ‹ˆλ‹€.

const f = <P extends IPropsFromParent & IPropsFromState>(component: Component<P>) => { /**/ }
const g = <P extends IPropsFromParent & IPropsFromState>(props: P) => { /**/ }

const record: IExampleProps = null

f(ExampleComponent)
g(record)

μ΄λŸ¬ν•œ μ΅œμ†Œν•œμ˜ μ˜ˆλŠ” μ‹€νŒ¨ν•©λ‹ˆλ‹€. 첫 λ²ˆμ§ΈλŠ” μœ„μ˜ μ½”λ“œμ™€ 같은 이유둜 μ‹€νŒ¨ν•˜κ³  λ™μΌν•œ 뢈투λͺ…ν•œ 였λ₯˜ λ©”μ‹œμ§€κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€. 두 λ²ˆμ§ΈλŠ” λΆ„λͺ…νžˆ IExampleProps reportType: ReportType 속성이 μ—†μœΌλ―€λ‘œ IExampleProps λ₯Ό P ν• λ‹Ήν•  수 μ—†κΈ° λ•Œλ¬Έμ— μ‹€νŒ¨ν•©λ‹ˆλ‹€. reportType κ°€ μ—†μŠ΅λ‹ˆλ‹€. λ‚˜λŠ” 이것이 λ‚΄λΆ€μ μœΌλ‘œ 첫 번째 μ˜ˆμ œκ°€ μ‹€νŒ¨ν•˜κ²Œ λ§Œλ“œλŠ” 원인이라고 μƒκ°ν•©λ‹ˆλ‹€.

5.0.16 μ»¨ν…Œμ΄λ„ˆκ°€ λž˜ν•‘λœ ꡬ성 μš”μ†Œμ˜ μΈν„°νŽ˜μ΄μŠ€μ— μƒˆ 속성을 μΆ”κ°€ν•  수 μ—†λŠ” κ²ƒμ²˜λŸΌ λ³΄μž…λ‹ˆκΉŒ? 이것은 λ‚΄κ°€ μ½”λ“œλ₯Όλ³΄κ³ μžˆλŠ” κ²ƒμž…λ‹ˆλ‹€.

ν•„μš”ν•œ μ†μ„±λ§Œ κΈ°μ€€μœΌλ‘œ ꡬ성 μš”μ†Œλ₯Ό μ •μ˜ν•œ λ‹€μŒ μƒˆ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜κ³  redux μ €μž₯μ†Œλ₯Ό 톡해 ꡬ성 μš”μ†Œμ˜ 속성을 μ œκ³΅ν•˜λŠ” HOC둜 ν•΄λ‹Ή ꡬ성 μš”μ†Œλ₯Ό ν™•μž₯ν•˜λŠ” 것이 일반적인 μ›Œν¬ν”Œλ‘œμ˜ μΌλΆ€μž…λ‹ˆλ‹€. μ–΄λ–€ κ²½μš°μ—λŠ” μƒˆ μΈν„°νŽ˜μ΄μŠ€κ°€ λž˜ν•‘λœ ꡬ성 μš”μ†Œ μΈν„°νŽ˜μ΄μŠ€μ˜ ν•˜μœ„ μ§‘ν•©μ΄μ§€λ§Œ λ‹€λ₯Έ κ²½μš°μ—λŠ” 그렇지 μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. μœ„μ˜ 경우 μƒˆ μΈν„°νŽ˜μ΄μŠ€μ—λŠ” ν˜ΈμΆœμžκ°€ μ‚¬μš©ν•˜μ§€λ§Œ λž˜ν•‘λœ ꡬ성 μš”μ†ŒλŠ” λ³Ό 수 μ—†λŠ” μΆ”κ°€ 속성 reportType 이 μžˆμŠ΅λ‹ˆλ‹€.


더 λ…νŠΉν•œ μ»¨ν…Œμ΄λ„ˆλŠ” μ»΄νŒŒμΌλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

이 λ³€κ²½μœΌλ‘œ 인해 μ†μƒλœ 기술의 또 λ‹€λ₯Έ μ˜ˆκ°€ μžˆμŠ΅λ‹ˆλ‹€.

두 개의 κ°„λ‹¨ν•œ ꡬ성 μš”μ†Œκ°€ μžˆλ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€.

class NameDateComponent extends React.PureComponent<{ name: string, date: string}> {}

class DateOnlyComponent extends React.PureComponent<{ date: string }> {}

name 속성에 λŒ€ν•΄ μ „ν˜€ μ•Œ ν•„μš” 없이 μ΄λŸ¬ν•œ ꡬ성 μš”μ†Œμ— λŒ€ν•΄ date λ₯Ό μ œκ³΅ν•˜λŠ” HOCλ₯Ό κ΅¬μΆ•ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ‚΄κ°€ ν•  μˆ˜μžˆλŠ”μ΄ λ°©λ²•μ€ν•˜λŠ” NameProviderκ³Ό DateProviderν•˜κ³ ,이처럼 그듀을 ꡬ성 : NameProvider(DateProvider(BaseComponent)) λ˜λŠ”μ΄ 같은 DateProvider(NameProvider(BaseComponent)) λ‚˜λŠ” 일반적으둜 잘 μž…λ ₯ ꡬ성 μš”μ†Œμ™€ ν•¨κ»˜ ν•  수 μ—†μ–΄, λ­”κ°€ TOwnProps λ₯Ό 지정해야 ν•˜κΈ° λ•Œλ¬Έμ— μΈν•Έμ„œκ°€ ν•„μš”ν•©λ‹ˆλ‹€.

interface IWithDate {
  date: string
}

// ComponenProps defines a component interface that includes IWithDate
// NewAPI defines a new interface that excludes IWithDate
// so the types represent only what will change in the given component, without needing to know the rest of the interface
function DateProvider <ComponentProps extends IWithDate, NewAPI extends Minus<ComponentProps, IWithDate>> (Base: CompositeComponent<ComponentProps>) {

  const enhancer = connect<ComponentProps, null, NewAPI>(
    (_state: IState, props: NewAPI): ComponentProps => {
      // because of the 'never' type, 
      // typescript doesn't think NewAPI & IWithProps == ComponentProps
      // so we have to coerce this (but you and I can see that the above holds)
      const newProps: any = Object.assign({
        date: '2017-01-01'
      }, props)

      return newProps as ComponentProps
    },
    null
  )

  return enhancer(Base) // <-- after 5.0.16 this line does not compile
}

이 μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€ λΆˆκ°€μ§€λ‘ μ  ꡬ성 μš”μ†Œ μΈν•Έμ„œλŠ” λ‹€μŒκ³Ό 같이 μ‚¬μš©λ©λ‹ˆλ‹€.

const NameOnly = DateProvider(NameDateComponent)
const Nothing = DateProvider(DateOnlyComponent)

.
.
.

<Nothing />
<NameOnly name='Bob' />
<NameDateComponent name='Bob' date='2017-01-01' />

λ”°λΌμ„œ DateProvider HOCλŠ” ν•΄λ‹Ή ꡬ성 μš”μ†Œμ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ™„μ „νžˆ μΈμ‹ν•˜μ§€ μ•Šκ³ λ„ ꡬ성 μš”μ†Œλ₯Ό 보강할 수 μžˆμŠ΅λ‹ˆλ‹€. 주어진 μ»΄ν¬λ„ŒνŠΈκ°€ μ œκ³΅ν•˜λŠ” propsλ₯Ό 받아듀일 수 μžˆλ‹€λŠ” κ²ƒλ§Œ μ•Œλ©΄ λ©λ‹ˆλ‹€.

5.0.16 ν•˜λ©΄ 더 이상 μž‘λ™ν•˜μ§€ μ•ŠμœΌλ©° λŒ€μ‹  λ‹€μŒ 였λ₯˜ λ©”μ‹œμ§€κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.

[ts]
Argument of type 'CompositeComponent<ComponentProps>' is not assignable to parameter of type 'ComponentType<ComponentProps & NewAPI>'.
  Type 'ComponentClass<ComponentProps>' is not assignable to type 'ComponentType<ComponentProps & NewAPI>'.
    Type 'ComponentClass<ComponentProps>' is not assignable to type 'StatelessComponent<ComponentProps & NewAPI>'.
      Types of property 'propTypes' are incompatible.
        Type 'ValidationMap<ComponentProps>' is not assignable to type 'ValidationMap<ComponentProps & NewAPI>'.
          Type 'keyof ComponentProps | keyof NewAPI' is not assignable to type 'keyof ComponentProps'.
            Type 'keyof NewAPI' is not assignable to type 'keyof ComponentProps'.
              Type 'keyof NewAPI' is not assignable to type '"date"'.

놀닀가 λ‹€μŒμ„ λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

interface IWithDate {
  date: string
}

interface IComponentProps {
  name: string
  date: string
}

// I've torn out the internals of the DateProvider above
const connect1 = <T extends IWithDate, U extends Omit<T, keyof IWithDate>>(base: CompositeComponent<T>) => {
  const enhancer: InferableComponentEnhancerWithProps<T & U, U> = () => null

  return enhancer(base) // <-- this is a compile error
}

const connect2 = <T extends IWithDate, U extends Omit<T, keyof IWithDate>>() => {
  const enhancer: InferableComponentEnhancerWithProps<T & U, U> = () => null

  return enhancer
}

const enhancer = connect2<IComponentProps, Omit<IComponentProps, keyof IWithDate>>()
const container = enhancer(NameDateComponent) // <-- this is not a compile error

컀λ„₯ν„°λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μž…λ ₯ν•˜λ©΄ λͺ¨λ“  것이 μž‘λ™ν•©λ‹ˆκΉŒ?


그게 λ‹€μ•Ό! μ½μ–΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€ ;) μ§€κΈˆμ€ @types/react-redux λ₯Ό 이전 λ²„μ „μœΌλ‘œ μ„€μ •ν–ˆμœΌλ©° λͺ¨λ“  것이 μ •μƒμž…λ‹ˆλ‹€.

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

5.0.15둜 λ˜λŒλ €λ„ λ¬Έμ œκ°€ μ™„μ „νžˆ ν•΄κ²°λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 5.0.15λŠ” κ²½κ³  없이 이런 μ’…λ₯˜μ˜ μ½”λ“œλ₯Ό μ»΄νŒŒμΌν•˜μ§€λ§Œ connect ν˜ΈμΆœμ—μ„œ ν”„λ¦¬μ  ν…Œμ΄μ…˜ ꡬ성 μš”μ†Œμ— λŒ€ν•œ μœ ν˜• 검사λ₯Ό μΆ”κ°€ν•˜λ©΄ μ—¬μ „νžˆ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€. (λž˜ν•‘ ꡬ성 μš”μ†Œμ— MyComponent μ—†λŠ” μ†Œν’ˆμ΄ μžˆλ‹€κ³  κ°€μ •)

예λ₯Ό λ“€μ–΄ 5.0.15μ—μ„œλŠ” μž‘λ™ν•˜μ§€λ§Œ 5.0.16μ—μ„œλŠ” μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
connect(stateToProps, dispatchToProps)(MyComponent)

κ·ΈλŸ¬λ‚˜ 5.0.15μ—μ„œλ„ 컴파일러 였λ₯˜ 없이 MyComponent propsλ₯Ό typecheckν•  수 μ—†μŠ΅λ‹ˆλ‹€.
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
λž˜ν•‘ ꡬ성 μš”μ†Œ μ†Œν’ˆμ΄ MyComponent μ—†λ‹€κ³  λΆˆν‰ν•˜λŠ” 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

λ‚˜λŠ” 이것이 @Pajn 의 정리가

λͺ¨λ“  3 λŒ“κΈ€

@variousauthors μ‚¬μš© 쀑인 @types/react 및 @types/react-redux 버전을 κ³΅μœ ν•΄ μ£Όμ‹œκ² μŠ΅λ‹ˆκΉŒ?

νŽΈμ§‘: @ 라이브러리의 이름이 ν‘œμ‹œλ˜μ§€ μ•Šμ•„ 엉망이 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ£„μ†‘ν•©λ‹ˆλ‹€. 싀망:

@variousauthors 이 λŒ“κΈ€μ΄

5.0.16 μ΄ν›„μ—λŠ” μ»¨ν…Œμ΄λ„ˆκ°€ λž˜ν•‘λœ ꡬ성 μš”μ†Œμ˜ μΈν„°νŽ˜μ΄μŠ€μ— μƒˆ 속성을 μΆ”κ°€ν•  수 μ—†λŠ” 것 κ°™μŠ΅λ‹ˆκΉŒ? 이것은 λ‚΄κ°€ μ½”λ“œλ₯Όλ³΄κ³ μžˆλŠ” κ²ƒμž…λ‹ˆλ‹€.

λ²„κ·ΈλŠ” redux의 connect ν•¨μˆ˜μ— λŒ€ν•œ μœ ν˜• μ •μ˜κ°€ ownProps 와 mapPropsToState ꡐ차 ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 이것은 λͺ¨λ“  "μ»¨ν…Œμ΄λ„ˆ" ꡬ성 μš”μ†Œκ°€ νŒŒμƒλœ "ν”„λ ˆμ  ν…Œμ΄μ…˜" ꡬ성 μš”μ†Œμ— 볡제된 propsκ°€ μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€. μ΄λŠ” 일반적인 redux λ””μžμΈ νŒ¨ν„΄μ„ κΉ¨λœ¨λ¦½λ‹ˆλ‹€.

μ†Œκ°œλœ PR: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24764
기타 문제: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/24922

5.0.15둜 λ˜λŒλ €λ„ λ¬Έμ œκ°€ μ™„μ „νžˆ ν•΄κ²°λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 5.0.15λŠ” κ²½κ³  없이 이런 μ’…λ₯˜μ˜ μ½”λ“œλ₯Ό μ»΄νŒŒμΌν•˜μ§€λ§Œ connect ν˜ΈμΆœμ—μ„œ ν”„λ¦¬μ  ν…Œμ΄μ…˜ ꡬ성 μš”μ†Œμ— λŒ€ν•œ μœ ν˜• 검사λ₯Ό μΆ”κ°€ν•˜λ©΄ μ—¬μ „νžˆ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€. (λž˜ν•‘ ꡬ성 μš”μ†Œμ— MyComponent μ—†λŠ” μ†Œν’ˆμ΄ μžˆλ‹€κ³  κ°€μ •)

예λ₯Ό λ“€μ–΄ 5.0.15μ—μ„œλŠ” μž‘λ™ν•˜μ§€λ§Œ 5.0.16μ—μ„œλŠ” μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
connect(stateToProps, dispatchToProps)(MyComponent)

κ·ΈλŸ¬λ‚˜ 5.0.15μ—μ„œλ„ 컴파일러 였λ₯˜ 없이 MyComponent propsλ₯Ό typecheckν•  수 μ—†μŠ΅λ‹ˆλ‹€.
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
λž˜ν•‘ ꡬ성 μš”μ†Œ μ†Œν’ˆμ΄ MyComponent μ—†λ‹€κ³  λΆˆν‰ν•˜λŠ” 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

λ‚˜λŠ” 이것이 @Pajn 의 정리가

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰