TypeScript:处理既不引用 state 也不引用 props 的“this”组件属性

TypeScript: dealing with `this` component properties which are reffering neither to state, nor to props

这是我的界面:

export type alphaColorProcessing = (opacity: number) => string | string[];

export interface welcomeMapping {
  [key: string]: alphaColorProcessing | string;
}
export interface IPalette {
  [key: string]: string | string[] | alphaColorProcessing | welcomeMapping;
}
export interface IThemeContext {
  theme: string;
  palette: IPalette;
  setTheme?: (theme: string) => void;
}

export interface IThemeState extends IThemeContext {
  isDark: Boolean;
}

export interface IAppState {
  loading: boolean;
  themeState: IThemeState;
}

export interface IAppProps {
  setTheme?: (theme: string) => void;
}

我在 App 组件中使用这些:

但我遇到了问题:在 this 上声明一个方法时,在构造函数中,据我所知它与 IAppState 无关。所以我的问题是,我如何 declare/use 一个既不引用 State 也不引用 Props 的方法的接口?我在组件的 this 上设置的 methods/properties 需要它。

netInfoSubscription, setTheme - 是我感兴趣的。

代码如下:

export default class App extends React.PureComponent<{},IAppState>  {
  showedForce = false;

  showedBadIP = false;

  constructor(props) {
    super(props);
    this.setTheme = (theme:string) => {
      this.setState((state) => ({
        themeState: {
          ...state.themeState,
          theme,
          isDark: theme === 'dark',
          palette: Palette,
        },
      }));
    };
    this.state = {
      loading: true,
      themeState: {
        theme: 'light',
        isDark: false,
        palette: Palette,
        setTheme: this.setTheme,
      },
    };
  }

  componentDidMount() {
    NetInfo.configure({
      reachabilityUrl: 'https://www.cisco.com/',
    });
    this.netInfoSubscription = NetInfo.addEventListener((state) => {
      handleConnectionStatus(state.isConnected);
    });
  }

  render() {
    const { themeState, loading } = this.state;
    if (loading) return null;
    return (
      <Provider store={ Store }>
        <AppearanceProvider>
          <SafeAreaProvider>
            <Root>
              <ThemeContext.Provider value={ themeState }>
                <Navigation />
                <FingerprintModal />
              </ThemeContext.Provider>
            </Root>
          </SafeAreaProvider>
        </AppearanceProvider>
      </Provider>
    );
  }
}

简单

在构造函数之外编写函数体

您问了两个相关的问题:setThemenetInfoSubscription

您有多种选择:

方法语法

你的setTheme是一个方法,所以你可以把它写成一个,然后在构造函数中使用bind

export default class App extends React.PureComponent<{},IAppState>  {
    constructor(props) {
        // ...
        this.setTheme = this.setTheme.bind(this);
        // ...
    }

    setTheme(theme: string) {
        // ...
    }
}

这样做的一个好处是 setThemeApp.prototype 上,这有时有助于单元测试(它可以被模拟)。

这适用于 setTheme,但不适用于 netInfoSubscription;为此,您需要以下其他选项之一。

属性 声明(包括方法)

你可以把你的setTheme写成属性(当然,netInfoSubscription也是属性):

export default class App extends React.PureComponent<{},IAppState>  {
    setTheme = (theme: string) => {
        // ...
    };
    netInfoSubscription?: SubscriptionType;
    // Or:
    netInfoSubscription: SubscriptionType | null = null;
}

对于setTheme,因为这是一个箭头函数,所以不需要绑定。它关闭的上下文 this 引用正在构造的实例。

对于netInfoSubscription,由于我们在实例构造时没有为它赋值,所以有两种选择:

  • 使用?表示属性是可选的。
  • 使用一个联合类型,其值类似于我们分配的 null until/unless 我们有订阅。

请注意,其中任何一个都意味着 netInfoSubscription 在代码中的任何地方都可能是 undefined(或 null)。对于 知道它不是的地方(但 TypeScript 不知道),你可以将它抓取到一个本地常量并做一个断言:

someMethod() {
    // If we *know* (from the logic) that we have the subscription now
    const { netInfoSubscription } = this;
    assertNotNullish(netInfoSubscription);
    // ...here you can use `netInfoSubscription`'s value, and TypeScript will understand
    // that we now know it has one
    // ...
}

assertNotNullish 是我的标准实用函数之一,看起来像这样:

const assertNotNullish: <T>(value: T | undefined | null) => asserts value is T = (value) => {
    if (value == null) { // Checks `null` and `undefined`
        throw new Error(`Expected value to be non-nullish`);
    }
};

Playground link

注意:可以只使用non-null assertion operator,但我不会。使用 assertNotNull 之类的东西会 runtime 检查你断言的内容是否正确。使用 non-null 断言运算符不会,它只是告诉 TypeScript 假定断言为真。

一个接口

您可以定义一个接口:

interface AppInstanceMembers {
    setTheme: (theme: string) => void;
    netInfoSubscription?: SubscriptionType;
    // ...
}
// ...
export default class App extends React.PureComponent<{},IAppState> & AppInstanceMembers {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^
    constructor(props) {
        // ...
        this.setTheme = (theme/* You don't have to repeat the type here*/) => {
            // ...
        };
        // ...
    }
    // ...
}

这告诉 TypeScript 在 App(您在构造函数中满足)上期望 setTheme(可能还有 netInfoSubscription)。但这确实意味着您必须重复 setTheme.

的某些部分

如前所示,在从逻辑中知道(但 TypeScript 不知道)netInfoSubscription 不会为空的地方,你可以使用 assertNotNullish.