跳到主要內容

使用 TypeScript 與 React Native

·8 分鐘閱讀
Ash Furrow
Artsy 的軟體工程師

JavaScript!我們都喜歡它。但我們有些人也喜歡型別。幸運的是,有許多選項可以為 JavaScript 新增更強大的型別。我最喜歡的是 TypeScript,但 React Native 開箱即用就支援 Flow。您偏好哪一個是個人喜好問題,它們各自有自己的方法來為 JavaScript 新增型別的魔力。今天,我們將看看如何在 React Native 應用程式中使用 TypeScript。

這篇文章使用 Microsoft 的 TypeScript-React-Native-Starter 存放庫作為指南。

更新:自這篇部落格文章撰寫以來,事情變得更加容易。您可以執行一個命令,即可取代這篇部落格文章中描述的所有設定

npx react-native init MyAwesomeProject --template react-native-template-typescript

然而,Babel 的 TypeScript 支援確實有一些限制,上面的部落格文章詳細介紹了這些限制。這篇文章中概述的步驟仍然有效,而 Artsy 仍然在生產環境中使用 react-native-typescript-transformer,但開始使用 React Native 和 TypeScript 的最快方法是使用上面的命令。如果必須,您可以隨時稍後切換。

無論如何,玩得開心!原始部落格文章繼續如下。

先決條件

由於您可能在多個不同的平台上開發,以多種不同類型的裝置為目標,因此基本設定可能會很複雜。您應該首先確保您可以執行沒有 TypeScript 的純 React Native 應用程式。按照 React Native 網站上的指示開始使用。當您成功部署到裝置或模擬器時,您就可以開始 TypeScript React Native 應用程式了。

您還需要 Node.jsnpmYarn

初始化

一旦您嘗試架設一個普通的 React Native 專案,您就可以開始新增 TypeScript 了。讓我們繼續執行此操作。

react-native init MyAwesomeProject
cd MyAwesomeProject

新增 TypeScript

下一步是將 TypeScript 新增到您的專案中。以下命令將會

  • 將 TypeScript 新增到您的專案
  • React Native TypeScript Transformer 新增到您的專案
  • 初始化一個空的 TypeScript 設定檔,我們將在下一步設定它
  • 新增一個空的 React Native TypeScript Transformer 設定檔,我們將在下一步設定它
  • 新增 React 和 React Native 的 類型定義

好的,讓我們繼續執行這些。

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 檔案包含 TypeScript 編譯器的所有設定。上述命令建立的預設值大致上都沒問題,但開啟檔案並取消註解以下行

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

rn-cli.config.js 包含 React Native TypeScript Transformer 的設定。開啟它並新增以下內容

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

遷移到 TypeScript

將產生的 App.js__tests_/App.js 檔案重新命名為 App.tsxindex.js 需要使用 .js 副檔名。所有新檔案都應使用 .tsx 副檔名(如果檔案不包含任何 JSX,則使用 .ts)。

如果您現在嘗試執行應用程式,您會收到類似 object prototype may only be an object or null 的錯誤。這是因為無法從 React 匯入預設匯出,也無法在同一行匯入具名匯出所造成的。開啟 App.tsx 並修改檔案頂端的匯入

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

其中一些與 Babel 和 TypeScript 如何與 CommonJS 模組交互操作的差異有關。未來,這兩者將在相同的行為上趨於穩定。

此時,您應該能夠執行 React Native 應用程式。

新增 TypeScript 測試基礎架構

React Native 隨附 Jest,因此為了使用 TypeScript 測試 React Native 應用程式,我們會想要將 ts-jest 新增到我們的 devDependencies 中。

yarn add --dev ts-jest

然後,我們將開啟我們的 package.json 並將 jest 欄位替換為以下內容

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

這會將 Jest 設定為使用 ts-jest 執行 .ts.tsx 檔案。

安裝相依性類型宣告

為了在 TypeScript 中獲得最佳體驗,我們希望類型檢查器能夠理解我們相依性的形狀和 API。某些函式庫將發布包含 .d.ts 檔案(類型宣告/類型定義檔案)的套件,這些檔案可以描述底層 JavaScript 的形狀。對於其他函式庫,我們需要明確地在 @types/ npm 範圍中安裝適當的套件。

例如,在這裡我們需要 Jest、React 和 React Native 以及 React Test Renderer 的類型。

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

我們將這些宣告檔案套件儲存到我們的開發相依性中,因為這是一個 React Native應用程式,僅在開發期間使用這些相依性,而不在執行階段使用。如果我們要將函式庫發布到 NPM,我們可能必須將其中一些類型相依性新增為常規相依性。

您可以在這裡閱讀更多關於取得 .d.ts 檔案的資訊。

忽略更多檔案

對於您的原始碼控制,您會想要開始忽略 .jest 資料夾。如果您使用 git,我們可以將條目新增到我們的 .gitignore 檔案中。

# Jest
#
.jest/

作為檢查點,請考慮將您的檔案提交到版本控制中。

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

新增組件

讓我們將組件新增到我們的應用程式中。讓我們繼續建立一個 Hello.tsx 組件。這是一個教學組件,不是您實際會在應用程式中編寫的組件,而是一些非平凡的組件,展示如何在 React Native 中使用 TypeScript。

建立一個 components 目錄並新增以下範例。

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

哇!這很多,但讓我們分解一下

  • 我們不是呈現像 divspanh1 等 HTML 元素,而是呈現像 ViewButton 這樣的組件。這些是在不同平台上運作的原生組件。
  • 樣式是使用 React Native 給我們的 StyleSheet.create 函式指定的。React 的樣式表允許我們使用 Flexbox 控制我們的版面配置,並使用其他類似於 CSS 中的結構來設定樣式。

新增組件測試

現在我們有了組件,讓我們嘗試測試它。

我們已經安裝了 Jest 作為測試執行器。我們將為我們的組件編寫快照測試,讓我們為快照測試新增所需的附加元件

yarn add --dev react-addons-test-utils

現在讓我們在 components 目錄中建立一個 __tests__ 資料夾,並為 Hello.tsx 新增一個測試

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

首次執行測試時,它將建立呈現組件的快照,並將其儲存在 components/__tests__/__snapshots__/Hello.tsx.snap 檔案中。當您修改組件時,您需要更新快照並檢查更新是否有意外變更。您可以在這裡閱讀更多關於測試 React Native 組件的資訊。

後續步驟

查看官方 React 教學課程和狀態管理函式庫 Redux。這些資源在編寫 React Native 應用程式時可能會有所幫助。此外,您可能還想看看 ReactXP,這是一個完全以 TypeScript 編寫的組件函式庫,同時支援網頁上的 React 和 React Native。

在更具型別安全性的 React Native 開發環境中玩得開心!