├── .editorconfig
├── .gitignore
├── .prettierrc
├── .watchmanconfig
├── App.js
├── README.md
├── app.json
├── assets
├── fonts
│ └── SpaceMono-Regular.ttf
└── images
│ ├── icon.png
│ ├── robot-dev.png
│ ├── robot-prod.png
│ └── splash.png
├── babel.config.js
├── jest.config.js
├── package.json
├── src
├── Main.test.tsx
├── Main.tsx
├── __snapshots__
│ └── Main.test.tsx.snap
└── components
│ ├── AppButton.android.tsx
│ └── AppButton.ios.tsx
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | # Share basic onfig accros editors
3 | # See http://editorconfig.org/
4 |
5 | [**.{js,jsx,ts,tsx,json}]
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 |
3 |
4 | *.iml
5 | .idea
6 | .expo
7 |
8 | build
9 |
10 |
11 | yarn-error.log
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "singleQuote": true,
5 | "tabWidth": 2,
6 | "useTabs": false,
7 | "bracketSpacing": true,
8 | "jsxBracketSameLine": false
9 | }
10 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 |
2 | import Main from './src/Main';
3 |
4 | export default Main;
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # expo-typescript
2 |
3 | Just a sample hello-world Expo project with:
4 |
5 | - Expo SDK >= 31
6 | - Babel 7
7 | - Typescript
8 | - Jest
9 | - Predefined `tsconfig.json` and `tslint.json`
10 | - `./src` root
11 | - Support for `.android.tsx` and `.ios.tsx`
12 | - Run concurrently `expo start` + `tsc --noEmit` (because babel typescript won't do the typechecking)
13 |
14 | Please don't hesitate to contribute or report anything that could be improved, as I'm a TS newbie I probably could have done something wrong.
15 |
16 |
17 | # Hire a freelance expert
18 |
19 | Looking for a React/ReactNative freelance expert with more than 5 years production experience?
20 | Contact me from my [website](https://sebastienlorber.com/) or with [Twitter](https://twitter.com/sebastienlorber).
21 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "expo-typescript",
4 | "description": "A very interesting project.",
5 | "slug": "expo-typescript",
6 | "privacy": "public",
7 | "sdkVersion": "31.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/images/icon.png",
12 | "splash": {
13 | "image": "./assets/images/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slorber/expo-typescript/ccea171478b561e228a0f390b8413f4ee8dccb79/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slorber/expo-typescript/ccea171478b561e228a0f390b8413f4ee8dccb79/assets/images/icon.png
--------------------------------------------------------------------------------
/assets/images/robot-dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slorber/expo-typescript/ccea171478b561e228a0f390b8413f4ee8dccb79/assets/images/robot-dev.png
--------------------------------------------------------------------------------
/assets/images/robot-prod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slorber/expo-typescript/ccea171478b561e228a0f390b8413f4ee8dccb79/assets/images/robot-prod.png
--------------------------------------------------------------------------------
/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slorber/expo-typescript/ccea171478b561e228a0f390b8413f4ee8dccb79/assets/images/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: [
5 | 'babel-preset-expo',
6 | "@babel/preset-typescript"
7 | ],
8 | plugins: [
9 | ["module-resolver", {
10 | "extensions": [
11 | ".js",
12 | ".jsx",
13 | ".ts",
14 | ".tsx",
15 | ".android.js",
16 | ".android.tsx",
17 | ".ios.js",
18 | ".ios.tsx",
19 | ],
20 | "root": [
21 | "./src"
22 | ]
23 | }],
24 | ]
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | preset: "jest-expo",
4 | moduleFileExtensions: ['js','jsx','json', 'ts', 'tsx'],
5 | transform: {
6 | "^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
7 | },
8 | testMatch: [
9 | "**/*.test.ts?(x)"
10 | ]
11 | };
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-new-project",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "concurrently \"yarn run start:expo\" \"yarn run start:typecheck\"",
7 | "start:expo": "expo start",
8 | "start:typecheck": "tsc --noEmit --watch",
9 | "android": "expo start --android",
10 | "ios": "expo start --ios",
11 | "eject": "expo eject",
12 | "test": "jest",
13 | "test:watch": "jest --watchAll",
14 | "lint": "tslint src/**/*.ts"
15 | },
16 | "dependencies": {
17 | "expo": "^31.0.2",
18 | "react": "16.5.0",
19 | "react-native": "https://github.com/expo/react-native/archive/sdk-31.0.0.tar.gz",
20 | "react-navigation": "^2.18.2"
21 | },
22 | "devDependencies": {
23 | "@babel/core": "^7.1.2",
24 | "@babel/preset-typescript": "^7.1.0",
25 | "@types/expo": "30.0.0",
26 | "@types/expo__vector-icons": "6.2.3",
27 | "@types/fbemitter": "2.0.32",
28 | "@types/jest": "^23.3.8",
29 | "@types/lodash": "^4.14.117",
30 | "@types/react": "16.4.18",
31 | "@types/react-native": "0.57.7",
32 | "@types/react-navigation": "2.13.0",
33 | "@types/react-test-renderer": "^16.0.3",
34 | "babel-core": "^7.0.0-bridge.0",
35 | "babel-plugin-module-resolver": "^3.1.1",
36 | "babel-preset-expo": "^5.0.0",
37 | "concurrently": "^4.0.1",
38 | "jest-expo": "^31.0.0",
39 | "tslint": "^5.11.0",
40 | "tslint-config-prettier": "^1.15.0",
41 | "typescript": "^3.1.6"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Main.test.tsx:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import * as React from 'react';
3 | import Main from 'Main';
4 | import * as renderer from 'react-test-renderer';
5 |
6 | describe('Main snapshot', () => {
7 | it('renders', () => {
8 | const tree = renderer.create().toJSON();
9 | expect(tree).toMatchSnapshot();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/Main.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {View, Text} from 'react-native';
3 | import AppButton from 'components/AppButton'
4 |
5 |
6 | interface State {
7 | message: string,
8 | count: number,
9 | }
10 |
11 | export default class Main extends React.Component<{}, State> {
12 | state = {
13 | message: "this is a typescript counter",
14 | count: 0,
15 | };
16 | componentDidMount() {
17 | console.debug("expo-typescript app mounted")
18 | }
19 | render() {
20 | return (
21 |
27 | {this.state.message}
28 |
29 | Count: {this.state.count}
30 |
31 | this.setState({count: this.state.count + 1})}
34 | />
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/__snapshots__/Main.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Main snapshot renders 1`] = `
4 |
14 |
15 | this is a typescript counter
16 |
17 |
18 | Count:
19 | 0
20 |
21 |
38 |
45 |
57 | increment (Android)
58 |
59 |
60 |
61 |
62 | `;
63 |
--------------------------------------------------------------------------------
/src/components/AppButton.android.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Button, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
3 |
4 | export interface ButtonProps {
5 | title: string
6 | onPress: (ev: NativeSyntheticEvent) => void
7 | }
8 |
9 | const AppButton = ({title, onPress}: ButtonProps) => (
10 |
14 | );
15 |
16 | export default AppButton;
17 |
--------------------------------------------------------------------------------
/src/components/AppButton.ios.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Button, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
3 |
4 | export interface ButtonProps {
5 | title: string
6 | onPress: (ev: NativeSyntheticEvent) => void
7 | }
8 |
9 | const AppButton = ({title, onPress}: ButtonProps) => (
10 |
14 | );
15 |
16 | export default AppButton;
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Target latest version of ECMAScript.
4 | "target": "esnext",
5 | // Search under node_modules for non-relative imports.
6 | "moduleResolution": "node",
7 | // Process & infer types from .js files.
8 | "allowJs": false,
9 | // Don't emit; allow Babel to transform files.
10 | "noEmit": true,
11 | // Enable strictest settings like strictNullChecks & noImplicitAny.
12 | "strict": true,
13 | // Disallow features that require cross-file information for emit.
14 | "isolatedModules": true,
15 | // Import non-ES modules as default imports.
16 | "esModuleInterop": true,
17 |
18 | "jsx": "react-native",
19 | "sourceMap": true,
20 | "rootDir": "src",
21 | "baseUrl": "./src",
22 |
23 | "noImplicitAny": true,
24 | "noUnusedLocals": true,
25 | "noUnusedParameters": true,
26 | "noImplicitReturns": true,
27 | "noImplicitThis": true,
28 | "importHelpers": true,
29 | "alwaysStrict": true,
30 | "forceConsistentCasingInFileNames": true,
31 | "strictFunctionTypes": true,
32 | "noFallthroughCasesInSwitch": true,
33 | "strictNullChecks": true,
34 | "allowSyntheticDefaultImports": true,
35 | "lib": [ "es6" ],
36 | "paths": {
37 | "*": ["*", "*.ios", "*.android"]
38 | }
39 | },
40 | "filesGlob": [
41 | "typings/index.d.ts",
42 | "src/**/*.ts",
43 | "src/**/*.tsx",
44 | "node_modules/typescript/lib/lib.es6.d.ts"
45 | ],
46 | "include": ["src/**/*"],
47 | "exclude":[
48 | "build",
49 | "node_modules",
50 | "jest.config.js",
51 | "src/App.js"
52 | ],
53 | "types": [
54 | "react",
55 | "react-native"
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint:recommended",
4 | "tslint-config-prettier"
5 | ],
6 | "rules": {
7 | "missing-jsdoc": false,
8 | "newline-before-return": false,
9 | "no-return-await": true,
10 | "object-literal-sort-keys": false,
11 | "interface-name": false,
12 | "no-console": false,
13 | "no-empty": false,
14 | "ordered-imports": false,
15 | "member-access": false
16 | }
17 | }
18 |
--------------------------------------------------------------------------------