Typescript: рдЬреЗрдиреЗрд░рд┐рдХ рдкрд░рд┐рднрд╛рд╖рд╛ рдореЗрдВ `рдПрдХреНрд╕рдЯреЗрдВрдб` рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЬреЗрдиреЗрд░рд┐рдХ рдЯрд╛рдЗрдк рдХреА рдЧрдИ рд╕рдВрдкрддреНрддрд┐ рдХреЗ рд▓рд┐рдП рд╕рдВрдЧрдд рдореВрд▓реНрдп рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдореЗрдВ рдЕрд╕рдорд░реНрдеред

рдХреЛ рдирд┐рд░реНрдорд┐рдд 7 рдЕрдкреНрд░реИрд▓ 2017  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: microsoft/TypeScript

рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╕рдВрд╕реНрдХрд░рдг: 2.2.2

рдореИрдВ рдмрд╕ рдЧрд▓рддрдлрд╣рдореА рд╣реЛ рд╕рдХрддреА рд╣реИ рдХрд┐ рдЬреЗрдирд░рд┐рдХ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ (рдпрджрд┐ рдРрд╕рд╛ рд╣реИ рддреЛ рдХреГрдкрдпрд╛ рдореБрдЭреЗ рд╕рд╣реА рджрд┐рд╢рд╛ рдореЗрдВ рдЗрдВрдЧрд┐рдд рдХрд░реЗрдВ), рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ рдмрдЧ рдЬреИрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИред

рдХреЛрдб

// A *self-contained* demonstration of the problem follows...

interface BaseState {
    on: boolean;
};

class Component {
    state: BaseState;

    setState(state: BaseState) {
        this.state = state;
    }

    onInput({ value }: { value: number }) {
        this.setState({ on: value > 0 });  // no error
    }
}

class GenericComponent<State extends BaseState> {
    state: State;

    setState(state: State) {
        this.state = state
    }

    onInput({ value }: { value: number }) {
        this.setState({ on: value > 0 });  // error Argument of type '{ on: boolean; }' is not assignable to parameter of type 'State'.
    }
}

рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░:
рдКрдкрд░ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ Component рд╡рд░реНрдЧ рдореЗрдВ BaseState рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХрд╛ рд╕реАрдзреЗ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдкрд░ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИред рдореИрдВ GenericComponent рд╡рд░реНрдЧ рд╕реЗ рд╕рдорд╛рди рд╡реНрдпрд╡рд╣рд╛рд░ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реВрдВ рдХреНрдпреЛрдВрдХрд┐ State рдЬреЗрдиреЗрд░рд┐рдХ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ BaseState рдлреИрд▓реА рд╣реБрдИ рд╣реИред

рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░:
рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдореБрдЭреЗ this.setState({ on: value > 0 }); рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдирд┐рдореНрди рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИ: "рдЯрд╛рдЗрдк рдСрдлрд╝ {on: рдмреБрд▓рд┐рдпрди;}" рдХрд╛ рддрд░реНрдХ 'рдЯрд╛рдЗрдк' рд╕реНрдЯреЗрдЯ рдХреЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд▓рд┐рдП рд▓рд╛рдЧреВ рдирд╣реАрдВ рд╣реИред "

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ рдХрд┐ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ рдпрджрд┐ рдЖрдк рдЗрд╕реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ

var t = new GenericComponent<{on: boolean, thing: string}>();
t.onInput({ value: 30 });
t.state.thing.substr(0); // 'thing' property should exist, but doesn't

рд╕рднреА 3 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ рдХрд┐ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ рдпрджрд┐ рдЖрдк рдЗрд╕реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ

var t = new GenericComponent<{on: boolean, thing: string}>();
t.onInput({ value: 30 });
t.state.thing.substr(0); // 'thing' property should exist, but doesn't

рдпрд╣рд╛рдБ рдПрдХ рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдг рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:

class Stateful<State extends { on: boolean }> {
  state: State = {
    on: true
  }; // error
}

рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдЗрд╕рдХрд╛ рдХрд╛рд░рдг рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдХрднреА рднреА рдХреЗрд╡рд▓ рддрднреА рдорд╛рдиреНрдп рд╣реЛрдЧрд╛ рдЬрдм Stateful рдХрд╛ рдПрдХ рдкреНрд░рдХрд╛рд░ рдХреЗ рддрд░реНрдХ рдХреЗ рд╕рд╛рде рддреНрд╡рд░рд┐рдд рд░реВрдк рд╕реЗ { on: boolean } рдмрд░рд╛рдмрд░ рд╣реЛред

рдЕрд╕рд▓ рдореЗрдВ, _there exists_ рдПрдХ рдЯрд╛рдЗрдк T , рдХреА рдХрдореА рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ Stateful рдХреЗ рдкреНрд░рдХрд╛рд░ рддрд░реНрдХ рдРрд╕реА рд╣реИ рдХрд┐ рдЗрдиреНрд╕реНрдЯреЗрдиреНрд╢рд┐рдпрд╢рди рдорд╛рдиреНрдп рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕ all_ рдкреНрд░рдХрд╛рд░ _for рдирд╣реАрдВ рд░рдЦрддрд╛ рд╣реИ T рдЬрд╣рд╛рдВ T Stateful рдкреНрд░рдХрд╛рд░ рдХреЗ рддрд░реНрдХ рдХреЛ рд╕рдВрддреБрд╖реНрдЯ рдХрд░рддрд╛ рд╣реИред

рддреЛ рдКрдкрд░ рдЧрд▓рдд рдХреЛрдб рд╕рднреА рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рд▓рд┐рдП T<T> рдЬрд╣рд╛рдВ T, рд╕реНрдЯреЗрдЯрдлреБрд▓ рд╣реИ`ред

рддрд╛рд░реНрдХрд┐рдХ рд░реВрдк рд╕реЗ, рд╣рдо рдЗрд╕реЗ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ

рдЕрднрд┐рдХрдерди

  1. P { on: boolean } рдЯрд╛рдЗрдк рд╣реЛрдиреЗ рджреЗрдВ
  2. рд╕рднреА рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рд▓рд┐рдП T рдРрд╕реЗ рд╣реИрдВ рдХрд┐ T P , Stateful<T> рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рд╡реИрдз рд╣реИред

рдХрд╛рдЙрдВрдЯрд░ рдЙрджрд╛рд╣рд░рдг рджреНрд╡рд╛рд░рд╛ рдкреНрд░рдЪреНрдЫрдиреНрди

  1. U { on: boolean, value: number } рдЯрд╛рдЗрдк рд╣реЛрдиреЗ рджреЗрдВ
  2. U рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░ P -> рдХреЗ рд▓рд┐рдП рдЙрдкрд▓рдмреНрдз рд╣реИ
  3. Stateful<U> рдЕрд╡реИрдз рд╣реИ -> рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрди рджреНрд╡рд╛рд░рд╛
  4. рдЗрд╕рд▓рд┐рдП T рдПрдХ рдкреНрд░рдХрд╛рд░ рдореМрдЬреВрдж рд╣реИ рдХрд┐ T P рдФрд░ Stateful<T> рдПрдХ рдЕрд╡реИрдз рддрд╛рддреНрдХрд╛рд▓рд┐рдХрддрд╛ рд╣реИ - рдирд┐рд╣рд┐рддрд╛рд░реНрде рджреНрд╡рд╛рд░рд╛

@RyanCavanaugh @aluanhaddad рд╢реВрдЯ, рдореБрдЭреЗ рдкрддрд╛ рдерд╛ рдХрд┐ред рдореИрдВ рдмрд╕ рдХреБрдЫ рдЖрд╕рд╛рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдорд╕реНрдпрд╛ рдХреЛ рджреВрд░ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реБрдП рднреНрд░рдорд┐рдд рд╣реЛ рдЧрдпрд╛ред рддреЛ рдЪрд▓рд┐рдП рдлрд┐рд░ рд╕реЗ рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдЖрдВрд╢рд┐рдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛? рдореБрдЭреЗ рдЕрднреА рднреА рдЗрд╕реА рддрд░рд╣ рдХреА рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИред

interface BaseState {
    on: boolean;
};

class Component {
    state: BaseState;

    setState(partialState: Partial<BaseState>) {
        this.state = { ...this.state, ...partialState };
    }

    onInput({ value }: { value: number }) {
        this.setState({ on: value > 0 });  // no error
    }
}

class GenericComponent<State extends BaseState> {
    state: State;

    setState(partialState: Partial<State>) {
        this.state = { ...this.state, ...partialState };
    }

    onInput({ value }: { value: number }) {
        this.setState({ on: value > 0 });  // error: Argument of type '{ on: boolean; }' is not assignable to parameter of type 'Partial<State>'
    }
}
рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

jbondc picture jbondc  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

Zlatkovsky picture Zlatkovsky  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

uber5001 picture uber5001  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

weswigham picture weswigham  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

Roam-Cooper picture Roam-Cooper  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ