├── .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 |