Typescript: Allow passing additional SupportedExtensions to support React Native .android and .ios module loading

Created on 27 Apr 2016  路  17Comments  路  Source: microsoft/TypeScript

We are using TypeScript to write our React Native application and encountered an issue with platform specific extensions provided by RN.

React Native will detect when a file has a .ios. or .android. extension and load the right file for each platform when requiring them from other components.

For example, you can have these files in your project:

BigButton.ios.js
BigButton.android.js

With this setup, you can just require the files from a different component without paying attention to the platform in which the app will run.

import BigButton from './components/BigButton';

When we use tsc to compile our code, it won't recognize the android or ios platform extension. What about have a customPlatform option that's similar to allowJs that auto prepend the platform name to supportedTypeScriptExtensions?

function getSupportedExtensions(options) {
        if (options && options.customPlatform) {
          return ts.supportedTypeScriptExtensions.concat(ts.supportedTypeScriptExtensions.map((ext) => `.${options.customPlatform}${ext}`))
        }
        return options && options.allowJs ? allSupportedExtensions : ts.supportedTypeScriptExtensions;
    }
Duplicate Suggestion

Most helpful comment

@mhegazy As far as I can tell (when testing it) the path mapping solution only applies to non-relative imports. So I cannot import A from '../otherfolder/stuff'. Am I missing something obvious here? Any help is appreciated

All 17 comments

I think this problem might be better solved with the glob pattern support in tsconfig.json (https://github.com/Microsoft/TypeScript/pull/5980). Modifying getSupportedExtensions wouldn't scale.

Makes sense. Love to see that change going in soon. Currently blocking our RN project.

Do not think globs will help here. the suggesion in the OP seems like a valid one. we should allow additional lookup locations, so that users can specify that, so "additionalExtensions" : [ "ios.ts", "ios.tsx", "android.ts", "android.tsx" ].

the workaround for now, would be create a .d.ts file next to your platform specific modules, something like:

/// components/BigButton.d.ts
export * from "./BigButton.ios"; 

this way your import for ./components/BigButton will resolve to ./components/BigButton.d.ts at design time getting you the shape of the module. and at runtime, it should do the right thing and bind to the platform specific version. there should not be any additional changes required.

@mhegazy If the exported types in BigButton.android.tsx diverges accidentally from BigButton.ios.tsx, then this won't be picked up at compile time, correct?

Is it possible to keep full compile time type checking?

The compiler does not know that BigButton.android.tsx and BigButton.ios.tsx are two implementation of the same module, and thus, should have the same shape. so they could diverge.

One possible solution here is to have modules implement interfaces, as previously suggested in https://github.com/Microsoft/TypeScript/issues/420. This will force the compiler to check the shape of the module against an interface.

here is another workaround to get it to type check today, courtesy of @RyanCavanaugh:

import * as ios from "./BigButton.ios"; 
import * as android from "./BigButton.ios"; 

declare var _test: typeof ios;
declare var _test: typeof android;

/// export to get the shape of the module
export * from "./BigButton.ios"; 

now if ios and android ever diverge, you will get an error on the redeclaration of _test. no code will be generated for this.

Recommendations here is to use path mapping support, see https://github.com/Microsoft/TypeScript-Handbook/blob/release-2.0/pages/Module%20Resolution.md#path-mapping

so your tsconfig.json should contain something like:

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

this will tell the compiler when resolving an import to BigButton to look at:

  • BigButton.tsx
  • BigButton.ios.tsx
  • BigButton.androdid.tsx

For checking that the different implementation for the modules all have the same shape is tracked by #420

closing in favor of #420

@mhegazy How can I use the paths compiler option? It isn't documented in the Compiler Options documentation.

Paths is a TS 2.0 feature. documentation is available at: https://github.com/Microsoft/TypeScript-Handbook/blob/release-2.0/pages/Module%20Resolution.md#path-mapping

should be merged in main documentation once 2.0 ships out.

@mhegazy As far as I can tell (when testing it) the path mapping solution only applies to non-relative imports. So I cannot import A from '../otherfolder/stuff'. Am I missing something obvious here? Any help is appreciated

Having the same issue as @sondremare. Is there an updated recommended way to deal with this particular RN feature?

Can't believe this issue still persists. The idea of allowing custom extension, or more specifically a _filename suffix support_, with ts or tsx at the end as in file.suffix.ts would be a great plus to the language.

@mhegazy This was closed in favour of https://github.com/Microsoft/TypeScript/issues/420, but as @sondremare mentioned the "paths" option doesn't work for relative imports, and #420 doesn't cover that. Any plans to address it?

Any plans to address it?

it is not clear what path mapping means with a relative path. so no plans really.,

i think that at this point the "umbrella" solution is the way to go.
On our index files we should tell to the compiler which components are needed at compile time.

having something like this on my index file (that is platform specific) for me makes sense:
index.ios.ts:
export * from "./BigButton.ios";

index.android.ts:
export * from "./BigButton.android";

Note that with webpack resolve.extensions this isn't necessarily a RN-specific issue, you could, for example have .ltr.tsx and .rtl.tsx variants, or .client.ts and .server.ts for isomorphic apps, etc..., essentially any time you may have multiple output bundles that should be using different code.

Was this page helpful?
0 / 5 - 0 ratings