Definitelytyped: لا يمكن استخدام "connect" الخاص بـ "رد فعل" كديكور: النوع "غير قابل للتخصيص لكتابة 'void'"

تم إنشاؤها على ٤ يوليو ٢٠١٦  ·  32تعليقات  ·  مصدر: DefinitelyTyped/DefinitelyTyped

يمكن أن تتسبب محاولة استخدام أداة رد الفعل-redux connect كمصمم فئة في ظهور رسائل خطأ من النموذج Type 'foo' is not assignable to type 'void' . يبدو أن المشكلة في هذه الكتابة هي أن TypeScript لا تسمح لـ ClassDecorator s بتغيير توقيع الشيء الذي تزينه ، ولكن هذا هو التنفيذ الحالي لكتابة رد الفعل والإعادة ويتم القيام به بشكل هادف ، منذ ذلك الحين يُرجع رد الفعل-الإعادة حالة مختلفة بتوقيع مختلف للدعامات .

استخدام connect كدالة يعمل كما هو متوقع ؛ إن استخدام الديكور فقط هو الذي يسبب مشاكل في الكتابة.

نرحب بالاقتراحات ، ولكن أي حلول مقترحة يجب أن تكون تحسينًا صارمًا لما يمكن أن تعبر عنه الكتابة حاليًا ، وهذا يعني أن التضحية بالصحة الحالية لأنواع الاستخدام كوظيفة غير مقبولة (جزئيًا لأن ذلك سيكون الانحدار الشديد ويرجع ذلك جزئيًا إلى أن المصممين يعتبرون "تجريبيين" ، وبالتالي ، أؤكد أنها ثانوية بالنسبة للاستخدام القياسي للوظيفة).

لا أعلم أن الحل الكامل لمشكلة كتابة الديكور ممكن بينما https://github.com/Microsoft/TypeScript/issues/4881 رائع. ومع ذلك ، هناك تحسينات تدريجية في هذه الحالة ، مثل (التمرير الأول) إخراج أي نوع من React.ComponentClass بحيث يتم تجميع الكود على الأقل ، ثم (التمرير الثاني) إخراج مكون يقبل تقاطع كل الدعائم (الخاصة والمتصلة أيضًا) ، على الرغم من أن ذلك سيكون متساهلاً للغاية ، لذا يتحقق نوع الكود على الأقل من بعض الدعائم.

الحل الوحيد الذي أملكه الآن هو عدم استخدام connect كديكور. قد يجده البعض قبيحًا من حيث الأسلوب ، لكنه يفحص النوع بشكل صحيح ، وليس أطول ويمكن استخدامه في أماكن أكثر من مصمم الديكور.

بدلا من:

@connect(mapStateToProps, mapDispatchToProps)
class MyComponent extends React.Component<...> { ... }

استخدم (شيء على غرار):

class MyComponentImpl extends React.Component<...> { ... }
const MyComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponentImpl);

لاحظ أن https://github.com/DefinitelyTyped/DefinitelyTyped/issues/8787 يدخل حيز التنفيذ هنا ويتم الخلط أحيانًا مع هذه المشكلة. قد ترغب أيضًا في تلميح نوع أو قالب للتأكد من أن مكون الإخراج له النوع المناسب.

تحرير: https://github.com/Microsoft/TypeScript/pull/12114 قد يساعد في هذه الحالة: من المحتمل أن تتم كتابة المصمم لإنشاء نسخة اختيارية بالكامل من الدعائم للمكون الذي تم إنشاؤه ، وهو ليس مثاليًا تمامًا ولكن يستمر في السماح لتطبيقك أن يكون أكثر حزماً بشأن بطلان الدعامة.

التعليق الأكثر فائدة

لاستخدام @connect مباشرة (لذلك بدون تقديم أدوات تزيين مخصصة) أستخدم الحل التالي:

@(connect(mapStateToProps, mapDispatchToProps) as any)
class MyComponent extends React.Component<...> {...}

لكنني أتفق تمامًا مع seansfkelley على أن أي كتابة ليست حقًا ما نريده ...

ال 32 كومينتر

كنت أواجه مشكلات مماثلة في ذلك اليوم وتركته من أجل وظيفة الاتصال. بدت الأنواع باستخدام أنواع التفاعلات القديمة. قد أحاول إصلاح هذا وإرسال العلاقات العامة؟

بالطبع! لقد تغير الكثير منذ أن تمت كتابة هذه الأنواع في الأصل ، على الرغم من أنني ما زلت متشككًا في أنه بدون دعم المتحول للديكور ، فمن الممكن أن يكون هناك شيء مفيد. لكنني عملت على التغلب على أوجه القصور الأخرى في نظام الكتابة من قبل ، لذا جربها.

ربما يمكن أن تجعلك الأنواع المعينة جزءًا من الطريق إلى هناك ، لذلك على الأقل لديك _بعض_ معلومات النوع المفيدة في الإخراج؟

يولو

export function myConnect(mapStateToProps, mapDispatchToProps) {
    return function (target) {
        return <any>connect(mapStateToProps, mapDispatchToProps)(target);
    }
}

لا أعتقد أن هناك الكثير من النقاط إلى كتابة yolo لهذا ، لأن الحلول المدرجة هنا وفي # 8787 ، على وجه التحديد تلك المتعلقة باستخدام تلميحات استدعاء الوظيفة ، ليست بهذا السوء (خاصة مع قوالب IDE لفئات المكونات) ووجود أي مكون مكتوب أمر محزن. :( أي اتصال للكتابة فقط لاستخدامه كديكور بدلاً من استدعاء وظيفة هو حقًا وضع العربة أمام الحصان.

لاستخدام @connect مباشرة (لذلك بدون تقديم أدوات تزيين مخصصة) أستخدم الحل التالي:

@(connect(mapStateToProps, mapDispatchToProps) as any)
class MyComponent extends React.Component<...> {...}

لكنني أتفق تمامًا مع seansfkelley على أن أي كتابة ليست حقًا ما نريده ...

على الرغم من أن بناء جملة المصمم رائع ، ما زلت أعتقد أن استخدام HOC مثل الاتصال بطريقة قياسية هو ممارسة أفضل.

1) يقوم بتعديل نوع الفئة أ يتعارض مع مواصفات المصمم.
2) كونيكت ليس مصمم ديكور على الإطلاق ، إنه ذو ترتيب ذو ترتيب أعلى.
3) يكسر قابلية نقل الكود من خلال وجود صيغة مختلفة للمكونات عديمة الحالة وذات الحالة.

لذلك أود أن أقول من فضلك ، لا تقدم تعريفًا يسمح لـ dev لاستخدامها بطريقة خاطئة :)

تضمين التغريدة أنت محق تمامًا ، استخدام HOCs كديكور ينتهك مواصفات الديكور. إنها ليست الطبقة الأساسية بعد الآن ولكنها مختلفة تمامًا.

أواجه نفس المشكلات بالضبط مع المصممين المخصصين وأتطلع إلى حالة دعمهم في TS (لا يزالون وراء العلم و AFAICS لا توجد خطط لتمكينهم افتراضيًا) أنا على وشك التخلي عن الدعم لـ معهم. علاوة على ذلك ، لا يزال المصممون في مشروع W3C.

أفضل طريقة لربط مكون بمكون جديد هو استخدام دالة ترتيب أعلى له بدلاً من محاولة _تزيين_ فئة موجودة.

لدي نفس الخطأ إذا قمت باستيراده. ومع ذلك ، فإن ما يلي يناسبني:

const { connect } = require('react-redux');

@connect(mapStateToProps, mapDispatchToProps)
class MyComponent extends React.Component<...> { ... }

TriStarGod هذا يعتمد على كيفية كتابتك require . من المحتمل أنك انتهيت من كتابة connect كـ any في هذه الحالة.

حل آخر / حل بديل.

نظرًا لأن لدي بالفعل حالة التطبيق الخاصة بي ، فأنا بحاجة إلى بعض الوظائف القصيرة جدًا في شكل:

export interface PageProps {
    routing: RouterState;
}

export interface PageDispatch {
    navigate: () => void
}

@connect<PageProps, PageDispatch>(
    state => ({
        routing: state.routing
    }),
    dispatch => ({
        navigate: () => dispatch(push("/"))
    })
)
export class Page extends React.Component<PageProps & PageDispatch> {
...
}

وهنا طريقة التوصيل المغلفة:

import { connect } from "react-redux";
import { ApplicationState } from './rootReducer';

interface MapPropsParam<TProps>{
    (state: ApplicationState, ownProps?: TProps): TProps
}

interface MapDispatchParam<TProps, TDispatchProps>{
   (dispatch: Redux.Dispatch<ApplicationState>, ownProps?: TProps): TDispatchProps;
}

export interface ConnectedComponent<TProps> {
    <TComponent extends React.ComponentType<TProps>>(component: TComponent): TComponent;
}

function connectToAppState<TProps, TDispatchProps = {}>(mapProps: MapPropsParam<TProps>, mapDispatch?: MapDispatchParam<TProps, TDispatchProps>) : ConnectedComponent<TProps> {
    return connect<TProps, TDispatchProps, TProps>(mapProps, mapDispatch) as ConnectedComponent<TProps & TDispatchProps>;    
}

export {
    connectToAppState as connect
}

شكرا لعملك. الاشتراك وانتظار التقدم في هذه القضية.

offbeatful يرجى تقديم إعلان عن نوع آخر من mapDispatchToProps

إذا تم تمرير كائن ، فمن المفترض أن تكون كل وظيفة بداخله منشئ إجراء إعادة التشغيل. كائن له نفس أسماء الوظائف ، ولكن مع كل منشئ إجراء ملفوف في استدعاء إرسال بحيث يمكن استدعاؤها مباشرة ، سيتم دمجه في خاصيات المكون.

حل بديل آخر استنادًا إلى offbeatful تعليق

myConnect.ts

import {
    connect as originalConnect, MapDispatchToPropsParam, MapStateToPropsParam, MergeProps, Options
} from "react-redux";
import * as React from "react";

export interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
    <TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>>(component: TComponent): TComponent;
}

interface MyConnect {
    <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps>,
        mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
    ): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>;

    <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps>,
        mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
        mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
        options?: Options<TStateProps, TOwnProps, TMergedProps>
    ): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;

}

export const connect = originalConnect as MyConnect;

تم التحديث بناءً على مقتطف pravdomil وأحدث الأنواع (5.0.13)

import { ApplicationState } from "./rootReducer";

import * as React from "react";
import {
    connect as originalConnect, MapDispatchToPropsParam, MapStateToPropsParam, MergeProps, Options
} from "react-redux";

export type InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> = <TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>>(component: TComponent) => TComponent;

interface MyConnect {
    <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, ApplicationState>,
        mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
    ): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>;

    <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, ApplicationState>,
        mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
        mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
        options?: Options<TStateProps, TOwnProps, TMergedProps>
    ): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;

}

export const connect = originalConnect as MyConnect;

مرحبًا offbeatful (cc: pravdomil أعتقد؟) ، لقد طرحت مؤخرًا سؤال Stack Overflow يتعلق بهذه المشكلة حيث تم التوصل إلى أن هذا غير مدعوم حاليًا. كجزء من هذا السؤال ، أعددت مستودعًا لعرض ما جربته. تم الاستنتاج أن هذا غير مدعوم حاليًا ، لذا كنت متحمسًا لرؤية مقتطف الشفرة الخاص بك اليوم ومضت قدمًا لمحاولة تحديث المستودع الخاص بي لاستخدام هذا.

هنا يمكنك رؤية الالتزام باستخدام مقتطف الشفرة المدمج

لم يعد هذا يصرخ بالأخطاء التي كنت أحصل عليها من قبل ، لكنني وجدت أنه في حالة الاستخدام الخاصة بي حيث يكون المكون الخاص بي متصلاً ، ولكن لديه أيضًا الدعائم الخاصة به ، فإن هذا التوقيع الجديد سيجعله الآن حتى تكون دعائم الحالة مطلوبة مثل الدعائم الخاصة (TSX). سيعرض cd my-app && yarn start في مستودعي ما أعنيه.

هل تعتقد أن هذا شيء يمكن معالجته أيضًا أم أنه غير ممكن؟

ربما يمكنك استخدام هذا الإصدار connect

connect(
        mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
        mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
        mergeProps: null | undefined,
        options: Options<State, TStateProps, TOwnProps>
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>

إذا كان الأمر كذلك ، فأنت تحتاج فقط إلى إصلاح المعلمة options لتكون Options<any, any, any> بهذه الطريقة لن يكون لديك مشكلة TStateProps & TDispatchProps لأن الأمر سيكون TStateProps & any

شيء صغير:

const options = { withRef:true } as Options<any, any, any>;
export const Component = connect(mapStateToProps, mapDispatchToProps, null, options)

رائع مثلك

TomasHubelbauer جربت
ستعمل بشكل غريب.

من الأفضل التأكد من استخدام الأنواع المشتركة بين الثلاثة:

export type CounterStateProps = {
    count: number;
};

export type CounterDispatchProps = {
    onIncrement: () => void;
};

export type CounterOwnProps = {
    initialCount: number;
};

export type CounterProps = CounterStateProps & CounterDispatchProps & CounterOwnProps;

ثم قم بتنفيذ المكون ذي الحالة

export class StatefulCounter extends React.Component<CounterProps, CounterStateProps> {
    timer: number;

    componentDidMount() {
        this.timer = setInterval(this.props.onIncrement, 5000);
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    render() {
      return (
        <StatelessCounter count={this.props.count}/>
      );
    }
}

وأخيرًا ، اجعل فئة الموصل مثل ذلك باستخدام بنية redux في connect وليس رمز الاتصال المخصص.

const mapStateToProps =
    (state: RootState, ownProps: CounterOwnProps): CounterStateProps => ({
        count: countersSelectors.getReduxCounter(state) + ownProps.initialCount,
    });

const mapDispatchToProps =
    (dispatch: Dispatch<CounterActionType>): CounterDispatchProps => bindActionCreators({
        onIncrement: CounterActions.increment,
    }, dispatch);

export const ConnectedCounter =
    connect(mapStateToProps, mapDispatchToProps)(StatefulCounter);

JohnHandley نعم ، أعلم أن هذا يعمل ، في المثال الخاص بي ، أوضحت أنه قبل أن أجرب بعض الاقتراحات لجعلها تعمل كمصمم ديكور. أستخدم البديل غير المصمم بنجاح ، لكني أرغب حقًا في جعل المصمم يعمل أيضًا.

أيضًا ، أعتقد أنك خلطت بين الأنواع الخاصة بك ، فأنت تستخدم CounterStateProps (وهو نوع الإرجاع mapDispatchToProps كمكون من CounterProps (وهو أمر جيد ، يحتوي على حالة إعادة التشغيل props و Redux dispatch props و JSX own props) ، ولكن أيضًا كنوع لحالة المكونات. بدلاً من ذلك ، يجب أن يكون لدى state نوعه الخاص الذي لا يشارك في توقيع نوع المكون الخارجي بأي شكل من الأشكال.

من المحتمل أنني لم أفهم الحل الخاص بك تمامًا ، لذلك إذا كان بإمكانك تنفيذ هذا العمل في مستودعي (حيث أستخدم كل من الدعائم الخاصة في دعائم حالة TSX و Redux) دون الحصول على خطأ يفيد بأنك بحاجة إلى تحديد دعائم حالة Redux في TSX ، أيضًا ، سيكون ذلك رائعًا!

+1 في هذه المسألة

لقد قمت بتحديث المقتطف من offbeatful وأنا أستخدمه الآن بنجاح.

connect.ts ( IAppState هي واجهة حالة الاستعادة)

import React from 'react'
import {
    connect as originalConnect,
    MapDispatchToPropsParam,
    MapStateToPropsParam,
    MergeProps,
    Options,
} from 'react-redux'


export type InferableComponentEnhancerWithProps<IInjectedProps, INeedsProps> =
    <IComponent extends React.ComponentType<IInjectedProps & INeedsProps>>(component: IComponent) => IComponent

export interface IConnect {
    <IStateProps = {}, IDispatchProps = {}, IOwnProps = {}>(
        mapStateToProps?: MapStateToPropsParam<IStateProps, IOwnProps, IAppState>,
        mapDispatchToProps?: MapDispatchToPropsParam<IDispatchProps, IOwnProps>,
    ): InferableComponentEnhancerWithProps<IStateProps & IDispatchProps, IOwnProps>

    <IStateProps = {}, IDispatchProps = {}, IOwnProps = {}, IMergedProps = {}>(
        mapStateToProps?: MapStateToPropsParam<IStateProps, IOwnProps, IAppState>,
        mapDispatchToProps?: MapDispatchToPropsParam<IDispatchProps, IOwnProps>,
        mergeProps?: MergeProps<IStateProps, IDispatchProps, IOwnProps, IMergedProps>,
        options?: Options<IStateProps, IOwnProps, IMergedProps>,
    ): InferableComponentEnhancerWithProps<IMergedProps, IOwnProps>

}

export const connect = originalConnect as IConnect

ثم يبدو المكون المتصل الخاص بي كما يلي:

import {connect} from 'path-to-my-connect/connect'

interface IOwnProps {
    ... props exposed for the real parent component
}

interface IStateProps {
    ... props from mapStateToProps
}

interface IDispatchProps {
    ... props from mapDispatchToProps
}

interface IProps extends IStateProps, IDispatchProps, IOwnProps {}

@connect<IStateProps, IDispatchProps, IOwnProps>(
    (state: IAppState) => {
            return {
                foo: getFoo(state), // getFoo is a selector using 'reselect'
            }
        },
    {setFoo, increment, decrement, ... your action creators},
)
class MyComponent extends React.PureComponent<IProps> {
        // your component implementation
}

export default (MyComponent as any) as React.ComponentType<IOwnProps>

ستفشل عملية الإرسال المباشر MyComponent as React.ComponentType<IOwnProps> ، لذا قمت بكتابتها كـ any أولاً. أعلم أنه اختراق ولكنه يعمل جيدًا للوالد وللمكون نفسه أيضًا.

هذا هو الحل الأكثر قابلية للتطبيق ، لكنه لا يزال مقيدًا ، وقد تمكنت من التوصل إليه.

"react-redux": "^5.0.6",
"typescript": "^2.8.1",
"@types/react-redux": "^6.0.0",

أى اخبار؟

ctretyak nope ، سيتعين عليك في النهاية إنشاء ملف إعلان بنفسك وتعديل المصمم. يبدو أن React Eco و TS ليسا أفضل الأصدقاء ، ولهذا السبب انتقلت من ذلك.

const DecoratedComponent = require('../DecoratedComponent').default;

يسمح هذا النوع من الاستيراد بعرض الدعائم IDE وإخفاء خطأ TS. قبيح بعض الشيء ، لكن الديكور يبدو أفضل من الاتصال البسيط. خاصة إذا قمت بالتكوين لإضافة بعض المحددات تلقائيًا مثل الترجمة أو التنقل

إنها ليست مشكلة رد فعل - إعادة. لدي نفس السلوك مع React -Router) وgraphql

يبدو أن المطبوع لا يفهم أن المصمم يمكنه حقن الدعائم ...

يبدو أن المطبوع لا يفهم أن المصمم يمكنه حقن الدعائم ...

لاف ، هذه لا تزال مشكلة؟ فقط اصطدمت بهذا.

أشعر بالحزن لأن شيئًا ما يدمر عقولنا ، لكنهم صُمموا ليجعلونا أكثر راحة:

أشعر بالحزن لأن شيئًا ما يدمر عقولنا ، لكنهم صُمموا ليجعلونا أكثر راحة:

أنا أيضا :(

ربما لا يغطي هذا جميع حالات الاستخدام ولكنه كان يعمل جيدًا بالنسبة لي.
كما أنه يمنحني فرصة لإضافة نوع متجري (MyAppStore) في مكان واحد.

export function connectTs<TDispatchProps={}, TOwnProps=any>(
  mapStateToProps?: (state: MyAppStore, ownProps?: TOwnProps) => any,
  mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
): any {
  return connect(mapStateToProps, mapDispatchToProps)
}

هل لديك أي تحديثات؟

أود استخدام الاتصال كمصمم ديكور أيضًا.

اي حلول رسمية مقدمة ؟؟؟
أعتقد أن هناك العديد من المطورين يفضلون استخدام connect بدلاً من الاتصال (mapStateToProps ، mapDispatchToProps) (المكونات)

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

alisabzevari picture alisabzevari  ·  3تعليقات

Loghorn picture Loghorn  ·  3تعليقات

victor-guoyu picture victor-guoyu  ·  3تعليقات

csharpner picture csharpner  ·  3تعليقات

jgoz picture jgoz  ·  3تعليقات