Definitelytyped: [@types/react]λŠ” μœ ν˜•μ΄ μ•ˆμ „ν•œ 동적 ν‚€ μ΄λ¦„μœΌλ‘œ setStateλ₯Ό μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

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

문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법을 μ•Œκ³  μžˆλ‹€λ©΄ λŒ€μ‹  ν’€ λ¦¬ν€˜μŠ€νŠΈλ₯Ό ν•˜μ‹­μ‹œμ˜€.

  • [x] @types/react νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•΄ λ³΄μ•˜λŠ”λ° λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
  • [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 ) λŒ€λ‹΅ν•˜λ‹€.

    • μ €μž: @johnnyreilly , @bbenezech , @pzavolinsky , @digiguru , @ericanderson , @morcerf , @tkrotoff , @DovydasNavickas , @onigoetz , @ theruther4d , @guilhermehubner , @ferdaber , @jrakoto

μž‘μ„±μžλ₯Ό μ–ΈκΈ‰ν•˜μ§€ μ•ŠμœΌλ©΄ λ¬Έμ œκ°€ λ¬΄μ‹œλ©λ‹ˆλ‹€.

type-safetyκ°€ μžˆλŠ” κ³„μ‚°λœ 속성 μ΄λ¦„μ—μ„œ μƒμ„±λ˜λŠ” 개체둜 setState λ₯Ό ν˜ΈμΆœν•  수 μ—†μŠ΅λ‹ˆλ‹€.

type State = {
  username: string,
  password: string
};

type StateKeys = keyof State;

class A extends React.Component<{}, State> {
  dynSetState(key: StateKeys, value: string) {
    this.setState({
      [key]: value // Error here. Pretty sure key is in StateKeys
    });
  }
}

#18365 및 https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment -351868649 의 ν•΄κ²° 방법을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ ν•΄κ²° 방법을 μ‚¬μš©ν•  λ•Œ TypescriptλŠ” λ‹€μŒκ³Ό 같은 경우 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

  dynLooselySetState(key: string, value: string) {
    this.setState(prevState => ({
      ...prevState,
      [key]: value // No error here, but can't ensure that key is in StateKeys
    }));
  }

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

이것은 컴파일러 자체의 ν•œκ³„μž…λ‹ˆλ‹€. κ³„μ‚°λœ 속성 ν‚€λ₯Ό 톡해 μœ μΆ”λœ μœ ν˜•μ€ 곡용체 μœ ν˜•μ„ μ§€μ›ν•˜μ§€ μ•ŠμœΌλ©° string , number , symbol λ˜λŠ” 이듀 쀑 ν•˜λ‚˜μ˜ λ¦¬ν„°λŸ΄λ§Œ μ§€μ›ν•©λ‹ˆλ‹€. 3, κ·Έ 쀑 ν•˜λ‚˜μ˜ μ‘°ν•©μž„μ„ κ°μ§€ν•˜λ©΄ 일반 string μœ ν˜•μœΌλ‘œ κ°•μ œ λ³€ν™˜ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ ν•΄κ²° 방법은 개체λ₯Ό κ°•μ œ λ³€ν™˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

dynSetState(key: StateKeys, value: string) {
  this.setState({
    [key]: value
  } as Pick<State, keyof State>)
}

값이 κ°€λŠ₯ν•œ 속성 κ°’ μœ ν˜• 집합에 μ—†μœΌλ©΄ μ—¬μ „νžˆ μ μ ˆν•˜κ²Œ 였λ₯˜κ°€ λ°œμƒν•˜μ§€λ§Œ ν‚€κ°€ κ°€λŠ₯ν•œ 속성 ν‚€ 집합에 μ—†μœΌλ©΄ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

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

이것은 컴파일러 자체의 ν•œκ³„μž…λ‹ˆλ‹€. κ³„μ‚°λœ 속성 ν‚€λ₯Ό 톡해 μœ μΆ”λœ μœ ν˜•μ€ 곡용체 μœ ν˜•μ„ μ§€μ›ν•˜μ§€ μ•ŠμœΌλ©° string , number , symbol λ˜λŠ” 이듀 쀑 ν•˜λ‚˜μ˜ λ¦¬ν„°λŸ΄λ§Œ μ§€μ›ν•©λ‹ˆλ‹€. 3, κ·Έ 쀑 ν•˜λ‚˜μ˜ μ‘°ν•©μž„μ„ κ°μ§€ν•˜λ©΄ 일반 string μœ ν˜•μœΌλ‘œ κ°•μ œ λ³€ν™˜ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ ν•΄κ²° 방법은 개체λ₯Ό κ°•μ œ λ³€ν™˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

dynSetState(key: StateKeys, value: string) {
  this.setState({
    [key]: value
  } as Pick<State, keyof State>)
}

값이 κ°€λŠ₯ν•œ 속성 κ°’ μœ ν˜• 집합에 μ—†μœΌλ©΄ μ—¬μ „νžˆ μ μ ˆν•˜κ²Œ 였λ₯˜κ°€ λ°œμƒν•˜μ§€λ§Œ ν‚€κ°€ κ°€λŠ₯ν•œ 속성 ν‚€ 집합에 μ—†μœΌλ©΄ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ •ν™•νžˆ @ferdaber λŠ” 이와 같은 IMHO μΊμŠ€νŒ…μ΄ 그닀지 "쒋은 νŒ¨ν„΄"이 μ•„λ‹ˆμ§€λ§Œ μ „λ°˜μ μœΌλ‘œ 클래슀 μ™ΈλΆ€μ—μ„œ ν•΄λ‹Ή μ½œλ°±μ„ ν…ŒμŠ€νŠΈν•˜κΈ° μ‰¬μš΄ μˆœμˆ˜ν•œ ν…ŒμŠ€νŠΈ κΈ°λŠ₯으둜 μΆ”μΆœν•˜λŠ” 것과 같은 λͺ¨λ²” 사둀λ₯Ό λ‹€μ‹œ μ€€μˆ˜ν•˜λŠ” 콜백 νŒ¨ν„΄μ„ 톡해 μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈν•˜λŠ” 것을 μ„ ν˜Έν•΄μ•Ό ν•©λ‹ˆλ‹€. :)

쒋은:

class C extends Component<{}, State> {
  updateState(key: StateKeys, value: string) {
    this.setState((prevState) => ({
      ...prevState,
      [key]: value,
    }));
  }
}

더 λ‚˜μ€:

const updateState = <T extends string>(key: keyof State, value: T) => (
  prevState: State
): State => ({
  ...prevState,
  [key]: value
})

class C extends Component<{}, State> {
  doSomething(){
    this.setState(updateState('password','123124'))
  }
}

typescript repoμ—μ„œ 이 μ œν•œμ— λŒ€ν•œ κ΄€λ ¨ 문제λ₯Ό 찾지 λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. 이것이 typescript repoμ—μ„œ λ…Όμ˜λ˜μ—ˆλŠ”μ§€(그리고 μ–΄λ””μ—μ„œ) μ•Œ 수 μžˆμŠ΅λ‹ˆκΉŒ? 기본적으둜 ν–₯ν›„ typescript λ²„μ „μ—μ„œ 이 문제λ₯Ό ν•΄κ²°ν•  κ³„νšμ΄ μžˆλŠ”μ§€ κΆκΈˆν•©λ‹ˆλ‹€.
감사 ν•΄μš”!

λ‹€μŒμ€ ν† λ‘ , TS νŒ€μ˜ λ””μžμΈ λ…ΈνŠΈ, μˆ˜μ • μ‹œλ„(ν–₯ν›„ λ²„μ „μ—μ„œλŠ” 철회된 κ²ƒμœΌλ‘œ 생각됨)μž…λ‹ˆλ‹€.
https://github.com/Microsoft/TypeScript/issues/13948
https://github.com/Microsoft/TypeScript/issues/18155
https://github.com/Microsoft/TypeScript/pull/21070

μ‹€μ œλ‘œ λ‚΄κ°€ λ‹¬μ„±ν•˜λ €λŠ” 것은 λ‹€μŒκ³Ό 같은 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. https://reactjs.org/docs/forms.html#handling -multiple-inputs
그러면 μ—¬λŸ¬ μž…λ ₯이 μžˆλŠ” 양식을 더 μ‰½κ²Œ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€. "name" 속성이 μš°λ¦¬κ°€ κΈ°λŒ€ν•˜λŠ” 것과 같은지 확인해야 ν•  μˆ˜λ„ μžˆμ§€λ§Œ κ·Έ ν›„μ—λŠ” μœ ν˜• μ•ˆμ „μ„±μ΄ μž‘λ™ν•΄μ•Ό ν•©λ‹ˆλ‹€.

@ferdaber 의 μ†”λ£¨μ…˜μ— as unknown λ₯Ό μΆ”κ°€ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

  this.setState({
    [key]: value
  } as unknown as Pick<State, keyof State>)

key κ°€ λΆ€μšΈμ΄λ©΄ κ²½κ³ ν•˜μ§€λ§Œ 숫자이면 κ²½κ³ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€!

κ·Έλž˜μ„œ μ €λŠ” 이 더 짧은 μ†”λ£¨μ…˜μ„ μ„ νƒν–ˆμŠ΅λ‹ˆλ‹€.

  this.setState<never>({
    [key]: value
  })

key κ°€ λΆ€μšΈ λ˜λŠ” 숫자인 경우 κ²½κ³ ν•©λ‹ˆλ‹€.

https://github.com/DefinitelyTyped/DefinitelyTyped/issues/26635#issuecomment -400260278이 μž‘λ™ν•˜λŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ? λ‚΄ 말은 ν‚€κ°€ μ—¬μ „νžˆ μœ λ‹ˆμ˜¨ μœ ν˜•μ΄λΌλŠ” λœ»μΈκ°€μš”?

이것이 당신을 λ„μšΈ κ²ƒμž…λ‹ˆλ‹€

type IAction = {
  [P in keyof IAppSettings]?: IAppSettings[P];
};

function reducer(state: IAppSettings, action: IAction) {
  return {
    ...state,
    ...action,
  };
}

이 λ¬Έμ œμ— 진전이 μžˆμŠ΅λ‹ˆκΉŒ?

λ‚˜λŠ” 이것을 μž‘λ™μ‹œν‚¬ 수 μžˆμ—ˆλ‹€

handleTextChange(name: keyof State, value: any) {
    this.setState({
        ...this.state,
        [name]: value
    });
}

이것을 보고 μžμ‹ μ˜ μƒνƒœ μœ ν˜•μ— μ μš©ν•˜λ €λŠ” λ‹€λ₯Έ μ‚¬λžŒλ“€μ„ μœ„ν•΄ 원본 κ²Œμ‹œλ¬Ό 및 μ‘λ‹΅μ˜ value μœ ν˜•μ€ State λͺ¨λ“  속성 μœ ν˜• λ•Œλ¬Έμ— string 에 State λŠ” string μž…λ‹ˆλ‹€. λ‹€λ₯Έ μœ ν˜•μ΄ 포함될 λ•Œ any λ˜λŠ” μΆ”κ°€ μΊμŠ€νŒ…μ„ μ‚¬μš©ν•˜λŠ” 것을 ν”Όν•˜κ³  λŒ€μ‹  value μœ ν˜•μ΄ ν‚€ μœ ν˜•κ³Ό μΌμΉ˜ν•˜λŠ” μœ ν˜•μ— ν•΄λ‹Ήν•˜λ„λ‘ μœ ν˜•μ„ μ œν•œν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

interface State {
  name: string;
  age: number;
}
type StateKeys = keyof State;
function dynSetState<K extends StateKeys>(key: K, value: State[K]) {
  this.setState({ [key]: value }); // fails; if only this worked...
  this.setState({ [key]: value } as Pick<State, K>); // clean cast works
  this.setState((s, _) => ({ ...s, [key]: value })); // avoids cast, but changes js
}
dynSetState("name", "a"); // works as expected
dynSetState("name", 1); // fails as expected
dynSetState("age", "a"); // fails as expected
dynSetState("age", 1); // works as expected

#26635(λŒ“κΈ€)이 μž‘λ™ν•˜λŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ? λ‚΄ 말은 ν‚€κ°€ μ—¬μ „νžˆ μœ λ‹ˆμ˜¨ μœ ν˜•μ΄λΌλŠ” λœ»μΈκ°€μš”?

의 μ’…λ₯˜ λ•Œλ¬Έμ— μž‘λ™ { ...prevState } μΆ©λΆ„νžˆ 일치 State μš°λ¦¬κ°€ κ΅­κ°€μ˜ μ΅œμ†Œν•œ λͺ¨λ“  속성이 기본적으둜 있기 λ•Œλ¬Έμ—. 그것 μ—†μ΄λŠ” _μž‘λ™ν•˜μ§€ μ•ŠλŠ”_ 이유λ₯Ό μ΄ν•΄ν•˜λŠ” 데 도움이 될 수 μžˆμŠ΅λ‹ˆλ‹€. 문제의 κ·Όλ³Έ 원인은 μƒμ„±λœ 개체의 μœ ν˜•μž…λ‹ˆλ‹€. { [key: K]: State[K] } κ°€ μΆ©λΆ„νžˆ ꡬ체적이지 μ•ŠμŠ΅λ‹ˆλ‹€. 그것은이닀 { [x: string]: State[K] } ν‚€ μΊμŠ€νŒ… ν•  μˆ˜μ—†λŠ” string Aλ₯Ό keyof State .

λ‹€λ₯Έ 예둜 @arinwt에 μΆ”κ°€ν•˜κΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€.

TL;DR: ν•˜λ‹¨μ˜ μ΅œμ’… μ†”λ£¨μ…˜

μ½˜μ†” 였λ₯˜κ°€ μžˆλŠ” ν™•μž₯된 μ†”λ£¨μ…˜:

type RegisterTypes = {
    email: string;
    password: string;
}

// ...

const [state, setState] = useState<RegisterTypes>({
    email: "",
    password: "",
});

// ...

const onChangeInput = (key: keyof RegisterTypes) => (event: React.ChangeEvent<HTMLInputElement>) => {
    setState({
        [key]: event.target.value
    } as Pick<RegisterTypes, typeof key>);
};

// ...

<input type="email" onChange={onChangeInput('email')} value={state.email} />

이것은 μ½˜μ†”μ—μ„œ λ‹€μŒκ³Ό 같은 였λ₯˜λ₯Ό μ œκ³΅ν•˜μ§€λ§Œ:

Warning: A component is changing a controlled input of type password to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

μ½˜μ†” 였λ₯˜κ°€ μžˆλŠ” λŒ€μ²΄ μ†”λ£¨μ…˜(μ†μž„μˆ˜):

경고도 ν•΄μ€€λ‹€.

type RegisterTypes = {
    [key: string]: string;
}

// ...

const onChangeInput = (key: string) => (event: React.ChangeEvent<HTMLInputElement>) => {
    setState({
        [key]: event.target.value
    });
};

μ΅œμ’… μ†”λ£¨μ…˜ 였λ₯˜ μ—†μŒ:

type RegisterTypes = {
    email: string;
    password: string;
}

// ...

const onChangeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    const newState = { ...state };
    newState[event.target.name as keyof RegisterTypes] = event.target.value;
    setState(newState);
};

// ...

<input name="email" type="email" onChange={onChangeInput} value={state.email} />
이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰