Definitelytyped: لا يمكن لـ [@ types / رد فعل] setState مع نوع اسم المفتاح الديناميكي الآمن

تم إنشاؤها على ١٨ يونيو ٢٠١٨  ·  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: في index.d.ts ) حتى يتمكنوا من ذلك رد.

    • المؤلفون: @ johnnyreilly ، benezech ، pzavolinsky ، digiguru ، ericanderson ، morcerf ، tkrotoff ، DovydasNavickas ، onigoetz ، @ theruther4d ، guilhermehubner ، ferdaber ،jrakotoharisoa

إذا لم تذكر المؤلفين ، فسيتم تجاهل المشكلة.

لا يمكنني الاتصال بـ setState مع إنشاء كائن من اسم خاصية محسوبة باستخدام type-safety:

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 على الرغم من أن

حسن:

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'))
  }
}

لم أجد مشكلة ذات صلة بهذا القيد في الريبو المطبوع على الورق .. هل هناك فرصة لتعرف ما إذا كان هذا قد تمت مناقشته (وأين) في الريبو المطبوع عليه؟ أتساءل بشكل أساسي عما إذا كانت هناك بعض الخطط لمعالجة هذا في الإصدارات المطبوعة في المستقبل.
شكرا!

ها هي المناقشة وملاحظات التصميم من فريق 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
مما يسهل التعامل مع نموذج ذي مدخلات متعددة. قد لا يزال يتعين علي التأكد من أن سمة "الاسم" هي ما نتوقعه ، ولكن بعد ذلك يجب أن تعمل سلامة النوع.

اضطررت إلى إضافة as unknown إلى حل ferdaber :

  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 في المنشور الأصلي والردود هو 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 إلى 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 التقييمات