natibe-base 2.8.0 の <content> で onScroll 使うと (実際には使えるにも関わらず) TSエラーになる

  • お世話になっているライブラリ NativeBase でおきていた TS エラーについて理解できてなかったので調べてみた

環境

  • react-natve: 0.57.8
  • natibe-base: 2.8.0
    • react-native-keyboard-aware-scroll-view: 0.5.0

ドキュメント

疑問

natibe-base 2.8.0 の <content>onScroll 使うと (実際には使えるにも関わらず) TS2339 エラーになるのはなぜか?

  • <Content onScroll={}> と書くと TSエラーになる

    • Error:(42, 9) TS2339: Property 'onScroll' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Content> & Readonly<{ children?: ReactNode; }> & R...'.
  • ドキュメントに書かれている事

    1. natibe-base の <content> コンポーネントreact-native-keyboard-aware-scroll-view の <KeyboardAwareScrollView> に変換される
    2. <KeyboardAwareScrollView>react-native の <ScrollView> のプロパティが使える
    3. <ScrollView>onScroll プロパティを持っている
  • <Content onScroll={}> は使い方として正しいよね?

  • わかっている事

    • onScroll={} 自体はちゃんと使えていて、動作している
    • Content の代わりに react-native の ScrollViewを使用すると TS エラーにならない
      • <ScrollView onScroll={}> と書いた場合は TS エラーにならない
    • Content の代わりに react-native-keyboard-aware-scroll-view' のKeyboardAwareScrollView を使用すると TS エラーにならない
      • <KeyboardAwareScrollView onScroll={}> と書いた場合は TS エラーにならない

(推測)

  • NateveBase に同梱されている TypeScript の 宣言ソースファイル(Declaration source file)(= node_modules/native-base/index.d.ts) になにか原因がありそう

コードを追ってみた

  • NativeBase の TypeScript の 宣言ソースファイル

    • node_modules/native-base/index.d.ts
  • NativeBase の Content コンポーネントソースコード

    • node_modules/native-base/src/basic/Content.js
  • NativeBase が利用している react-native-keyboard-aware-scroll-view の TypeScript の 宣言ソースファイル

    • node_modules/native-base/node_modules/react-native-keyboard-aware-scroll-view/index.d.ts

わかった事

  • <Content onScroll={}> は有効

  • native-base の index.d.ts (型定義ファイル) に不備がある

    • Content の interface が react-native-keyboard-aware-scroll-view の型定義ファイルを extend していない
    • この為、KeyboardAwareScrollView のプロパティ(ひいては ScrollView のプロパティ)を使うと、TS エラーになる
  • react-native-keyboard-aware-scroll-view の index.d.ts には不備がない

    • interface KeyboardAwareScrollViewProps は react-native の ScrollViewProperties を extends している
// node_modules/native-base/index.d.ts

declare module "native-base" {

// KeyboardAwareScrollViewProps は import されてない
    import * as React from "react";
    import * as ReactNative from "react-native";

    namespace NativeBase {

// node_modules/native-base/index.d.ts:159
// ★ここを Content extend KeyboardAwareScrollViewProps とすれば、onScroll が TS エラーにならない
        /**
         * see Widget Content.js
         */
        interface Content {
            /**
             * The theme prop can be applied to any component of NativeBase.
             */
            refreshing?: boolean;
            refreshControl?: object;
            theme?: Object;
            padder?: boolean;
            disableKBDismissScroll?: boolean;
            enableResetScrollToCoords?: boolean;
            contentOffset?: Object;
            scrollEnabled?: boolean;
            style?: ReactNative.ViewStyle | Array<ReactNative.ViewStyle>;
            contentContainerStyle?: ReactNative.ViewStyle | Array<ReactNative.ViewStyle>;
            keyboardShouldPersistTaps?: string;
                keyboardDismissMode?: string;
        }
    }
// node_modules/native-base/src/basic/Content.js

import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";

  render() {

// 確かに `react-native-keyboard-aware-scroll-view` の `<KeyboardAwareScrollView>` コンポーネントを `render()` している
    return variable.isIphoneX ? (
      <KeyboardAwareScrollView
      >
        {this.props.children}
      </KeyboardAwareScrollView>
    ) : (
      <KeyboardAwareScrollView
      >
        {this.props.children}
      </KeyboardAwareScrollView>
    );
// node_modules/native-base/node_modules/react-native-keyboard-aware-scroll-view/index.d.ts

import { ScrollViewProperties, ListViewProperties, FlatListProperties } from 'react-native'

// react-native の `ScrollViewProperties` を extends している
interface KeyboardAwareScrollViewProps
  extends KeyboardAwareProps,
    ScrollViewProperties {}

export class KeyboardAwareScrollView extends React.Component<
  KeyboardAwareScrollViewProps,
  KeyboardAwareState
> {}

対応方法

  • native-base に下記の修正を Pull Request する

    • 型定義ファイルが DefinitelyTyped でモジュール本体と別に提供されてはおらず、(※1)
    • パッケージに同梱されている場合は、
    • パッケージのリポジトリに Pull Request を送って取り込んでもらう他ない
  • 修正内容

    • index.d.ts
      • Content の interface を KeyboardAwareScrollViewProperties を extends したものに修正
//追加
import {KeyboardAwareScrollViewProperties} from "react-native-keyboard-aware-scroll-view";
    
// interface Content { 
//  ↓ 下記のように修正
interface Content extends KeyboardAwareScrollViewProperties {

早速 Pull Request させて頂きました

※1 @type/{パッケージ名} についてメモ

  • DefinitelyTyped で対象ライブラリの型定義が提供されている場合

    • $ yarn add @types/{パッケージ名} --dev で型定義をライブラリ本体とは別にインストールする
  • 例えば、react-native は DefinitelyTyped/types/react-native/ でライブラリ本体とは別のモジュールとして型定義を提供している

    • $ yarn add @types/react-native --dev で react-native の型定義をインストールできる