18
votes

I am using react native with typescript. I have a component with the following structure

component
    - component.ios.tsx
    - component.android.tsx

Now I want to import the component. So, I am doing this:

import component from '../component/component';

But it says:

[ts] Cannot find module ../component/component

The location is correct. I need to add something to the tslint file to make it understand.

I also tried to do:

let component = require('../component/component');

This didn't give typescript error. But it gave a runtime error.

element type is invalid expected a string or a class/function but got undefined

Has anybody run into this issue? Thank you.

4

4 Answers

23
votes

One way of doing it, which is a bit annoying, is creating a declaration file with the same name.

component
- component.d.ts   <---
- component.android.tsx
- component.ios.tsx

then

import { file } from "../group/file";

Update: Another way of doing it is just omit the extension for one of them, then typescript will pick up the default one.

  • component.tsx
  • component.android.tsx

Android will pick up the specific android file, and iOS will default to normal one.

5
votes

With last version of React you can use Suspense and lazy to avoid over typings etc., for example if I want a component Touchable with specific code for iOS and Android, my structure will looks like that:

- Touchable
   - index.tsx
   - Touchable.android.tsx
   - Touchable.ios.tsx
   - types.d.ts
   

And on index.tsx the code will looks like that:

import React, { FunctionComponent, lazy, Suspense } from 'react';
import { Platform, View } from 'react-native';

import { TouchableProps } from './types.d';

const TouchableComponent = lazy(() =>
  Platform.OS === 'ios'
    ? import('./Touchable.ios')
    : import('./Touchable.android'),
);

const Touchable: FunctionComponent<TouchableProps> = (props) => {
  return (
    <Suspense fallback={<View />}>
      <TouchableComponent {...props} />
    </Suspense>
  );
};

export default Touchable;

So anywhere on my app I want to use this component, I just have to do that:

import Touchable from './path/to/Touchable';

[...]
<Touchable>
  <Text>Touchable text</Text>
</Touchable>
[...]

Types.d.ts :

export type TouchableSizeType = 'small' | 'regular' | 'big';
export type TouchableType = 'primary' | 'secondary' | 'success' | 'error' | 'warning';

export interface TouchableProps {
  disabled?: boolean;
  size?: TouchableSizeType;
  type?: TouchableType;
  onClick?: (event?: Event) => Promise<void>;
}
1
votes

It all makes sense, since file at ../component/component.tsx does not exists.

Haven't tried, but this could learn TS to understand such imports

{
    "compilerOptions": {
        "paths": {
            "*": ["*", "*.ios", "*.android"]
        }
   }
}
1
votes

Lets say that your component name is ProgressBar, so create index.d.ts and adjust your code below in index.d.ts

import {Component} from 'react';
import {Constructor, NativeMethodsMixin} from 'react-native';

export interface ProgressBarProps {
  progress: number;
}

declare class ProgressBarComponent extends Component<ProgressBarProps> {}

declare const ProgressBarBase: Constructor<NativeMethodsMixin> &
  typeof ProgressBarComponent;

export default class ProgressBar extends ProgressBarBase {}

So your component folder should looks like

component/
 - index.android.tsx
 - index.ios.tsx
 - index.d.ts