Description
Proposal is to add a new command line parameter to tsc which provides a list of resolutionPlatforms
. This is just an array of strings, that will be used during that invocation of tsc to do module resolution.
You can then during your build invoke tsc once for each platform to do a complete static type check of the files that will actually get included by the metro-bundler.
This small change will allow developers to take existing typescript react-native projects which are not being properly typechecked due to metro-bundler's platform specific file resolution.
We have run this on our repo and found various bugs with the additional type checking.
This fixes the issue described in
#17681 which was closed due to no actionable solution.
The code for such a new parameter is relatively easy to add to typescript:
Here is a link to a branch based on 2.7.1 with the new parameter implemented.
acoates-ms@f023961
It also solves #8328 which has an incorrect solution of using the paths option, which doesn't work in a bunch of cases, such as relative path imports.
and was incorrectly marked as a dup of #420 , which isn't the same as that forces all the platforms to expose the same interface, which isn't what the bundler does.
Example build script to check multiple platforms:
/*
By default tsc doesn't do platform resolution the same way as metro-bundler.
This means that tsc wasn't checking the code exactly as its going to be in the bundle.
This script manually runs tsc against the various platforms index entry points,
modifiying the resolution platforms parameter according to the platform resolution
rules of that platform.
We can't just have the tsconfig do the right thing, as it can only have one moduleResolution option.
*/
let path = require("path");
const fs = require("fs");
const execSync = require("child_process").execSync;
function createCustomTsconfig(rootPath, entryFile, platforms) {
const tsconfig = JSON.parse(fs.readFileSync(path.resolve(rootPath, "tsconfig.json"), "utf8"));
tsconfig.compilerOptions.resolutionPlatforms = platforms;
tsconfig.compilerOptions.noEmit = true;
tsconfig.files = [path.resolve(rootPath, entryFile)];
let cmdParams = "";
for (var _ in tsconfig.compilerOptions) {
if (typeof tsconfig.compilerOptions[_] == typeof true) {
if (tsconfig.compilerOptions[_]) {
cmdParams += `--${_} `;
}
} else if (typeof tsconfig.compilerOptions[_] == typeof "string") {
cmdParams += `--${_} ${tsconfig.compilerOptions[_]} `;
} else {
cmdParams += `--${_} ${tsconfig.compilerOptions[_].join(",")} `;
}
}
cmdParams += path.resolve(rootPath, entryFile);
const cmd = `node ${path.resolve(rootPath, "../../node_modules/typescript/lib/tsc.js")} ${cmdParams}`;
console.log(cmd);
const output = execSync(cmd, { stdio: [0, 1, 2] });
}
createCustomTsconfig(path.resolve(__dirname, "../"), path.resolve(__dirname, "../index.mobile.ts"), ["ios", "native"]);
createCustomTsconfig(path.resolve(__dirname, "../"), path.resolve(__dirname, "../index.mobile.ts"), ["android", "native"]);
TypeScript Version: 2.7.1
Search Terms: react-native platform resolution
Code
A.ts
import { MyComponent } from 'B';
export class AComponent extends React.Component<{}, {}> {
public render() {
<BComponent foo={ 1 } />
}
}
B.ts
export class BComponent extends React.Component<{ foo: number }, {}> {
public render() { ... }
}
B.ios.ts
export class BComponent extends React.Component<{ foo: number, bar: string }, {}> {
public render() { ... }
}
Expected behavior:
Type error when compiling for ios, since AComponent is not providing the required bar property.
Actual behavior:
tsc never checked B.ios.ts at all. -- So the code that gets included in the ios react-native bundle never gets checked as bundled.
Related Issues:
One issue this doesn't solve is cross package react-native platform resolution. If package C has A and B as above, but A exported B. Then A.d.ts would likely contain the incorrect information as its not clear
which version of B it would include. I think the next evolution of this change would be to have tsc output a separate A.ios.d.ts and A.android.d.ts when doing the platform specific builds. Thus ensuring that cross package type checking can also be done. This shouldn't be too hard to add, and would be completely additive to this change.