毫无疑问,TypeScript已经成为许多开发者的最爱,在npm ,每周有超过1900万次的下载,你有可能在每三个与JavaScript相关的工作描述中看到Typescript。
在这篇文章中,我们将研究如何在你的React Native应用程序中使用TypeScript。在本文结束时,你会很自如地利用TypeScript的优势来减少错误,并在你的应用程序中添加类型检查。
什么是TypeScript?
TypeScript是微软在JavaScript基础上建立的一种开源语言。你可以把 TypeScript 看作是 JavaScript,但有静态类型定义。
为什么你应该考虑 TypeScript
如果本文的前两段还不足以说服你尝试 TypeScript,这里还有几个原因:
- 更好的文档。有了明确定义的类型,你可以很容易地知道如何引用你的代码的其他部分
- 减少错误。TypeScript可以在执行前验证你的代码,因此可以节省大量时间来调试为什么 "undefined不是第23行的一个函数"

- 编译成JavaScript。因为TypeScript是JavaScript的超集,它可以编译为JavaScript,所有有效的JavaScript都是有效的TypeScript。
- 易于采用。如果你曾经想把TypeScript添加到你现有的JavaScript应用程序中,你可以轻松地从一个文件开始,看看它是如何发展的
用React Native和TypeScript构建一个应用程序
现在我们知道了TypeScript是什么,我们可以开始研究如何在我们的React Native应用程序中使用它。
在本教程中,我们将建立一个购物清单应用程序。这是一个很好的方法,可以看到TypeScript如何改善你的开发经验,而不会让事情变得太复杂。
要求
- 运行CLI命令的基本知识
- 在你的机器上安装Node.js和npm
- 在你的机器上安装XCode或Android Studio
- 具备JavaScript的基本知识
- React的基本知识
- 对React Native有一定的经验(建议,不是必须)。
开始工作
让我们开始吧,好吗?在你的终端运行下面的命令。
npx react-native init ShoppingList --template react-native-template-typescript
上面的命令将建立一个新的React Native项目,该项目使用TypeScript模板,在一个名为ShoppingList 的文件夹中包含所需的依赖和配置。
要了解如何在现有的应用程序中使用TypeScript,请看一下文档。
构建基础UI
在你的代码编辑器中打开该文件夹,并运行下面的命令(取决于你的机器)来启动该项目。
# MacOS
npm run ios
# Windows/Linux
npm run android
App.tsx 组件是我们应用程序中的根组件。这个组件将包含我们在应用程序中使用的其他组件,我们将在稍后回到它。现在,让我们来创建我们的第一个组件。
在根目录下创建一个类似src/components 的文件夹结构,并在components 文件夹中创建一个Header.tsx 组件。
你是否注意到,我们创建的文件有一个.tsx 的扩展名?那是因为我们现在是用TypeScript构建,我们需要使用适当的文件扩展名,否则我们会在普通的.js 文件中出现错误。
将下面的代码粘贴到文件中。
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
interface Props {
title: string;
}
const Header: React.FC<Props> = ({title}) => {
return (
<View style={styles.header}>
<Text style={styles.headerText}>{title}</Text>
</View>
);
};
const styles = StyleSheet.create({
header: {
paddingVertical: 20,
borderBottomWidth: 1,
borderBottomColor: '#cfcfcf',
alignItems: 'center',
},
headerText: {
fontSize: 20,
fontWeight: '600',
},
});
export default Header;
如果你已经熟悉了React Native,请忽略上面的大部分代码;你会知道我们在这里要做什么。然而,我想提请你注意第3-6行。
interface Props {
title: string;
}
const Header: React.FC<Props> = ({title}) => { /* content here*/}
因为我们使用的是TypeScript,我们现在能够定义我们的组件应该接受什么,应该如何接受,以及接受多少。
在前三行中,我们声明了一个接口,它是我们通常在组件中访问的props 对象的结构。然而,这一次我们使用TypeScript来指定道具和它们的类型。
这样做的好处是,我们可以得到更好的IntelliSense和一些验证,当我们使用该组件时(例如,当title 没有传递给该组件时,会有一个即时错误)。
回到App.tsx 组件中,用下面的代码替换内容:
import React from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
const App = () => {
return (
<SafeAreaView style={styles.container}>
<Header />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#e8e7e3',
},
});
export default App;
你应该注意到在<Header/> 组件下面有一条红线。如果你把鼠标悬停在它上面,你应该看到一条信息,即该组件期望有一个title ,但没有提供这个道具。现在让我们来做这件事。
用下面的代码片断替换这一行,你应该看到错误已经消失了。
<Header title="Shopping List" />
如果你试图分配一个数字(或任何其他不是字符串的数据类型),你会得到一个不同的错误。这是TypeScript试图帮助你在错误发生之前抓住它们。
将项目添加到列表中
创建一个新的组件,AddItem.tsx ,到你的/src/components 文件夹,然后粘贴下面的代码:
import React, {useState} from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
} from 'react-native';
export interface IItem {
item: string;
quantity: string;
}
const AddItem = () => {
const [item, setItem] = useState('');
const [quantity, setQuantity] = useState('');
return (
<View>
<Text style={styles.heading}>Add Shopping Item</Text>
<View style={styles.form}>
<TextInput
style={styles.input}
placeholder="Enter item"
value={item}
onChangeText={text => setItem(text)}
/>
<TextInput
style={styles.input}
placeholder="Enter quantity"
keyboardType="numeric"
value={quantity}
onChangeText={q => {
setQuantity(q);
}}
/>
<TouchableOpacity style={styles.addItemButton} onPress={() => {}}>
<Text style={styles.buttonText}>Add Item</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
heading: {
fontSize: 20,
fontWeight: '700',
},
form: {
marginTop: 30,
},
input: {
padding: 15,
borderColor: 'rgba(0, 0, 0, 0.2)',
borderWidth: 1,
borderRadius: 5,
marginBottom: 20,
},
addItemButton: {
backgroundColor: '#eb8634',
paddingVertical: 20,
borderRadius: 5,
alignItems: 'center',
},
buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;
注意到第9行中命名的导出IItem ?那是我们购物清单中的一个项目的结构,我们导出它是因为我们将在其他组件中需要它。
回到App.tsx ,用下面的代码更新该组件:
import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem'; /* import AddItem and interface*/
const App = () => {
const [shoppingList, setShoppingList] = useState<IItem[]>([]); // set the type of what the hook expects to be an array of IItems.
return (
<SafeAreaView style={styles.container}>
<Header title="Shopping List" />
<View style={styles.contentWrapper}>
<AddItem
setShoppingList={setShoppingList}
shoppingList={shoppingList}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#e8e7e3',
},
contentWrapper: {
padding: 20,
},
});
export default App;
现在在App.tsx ,我们导入了新的AddItem 组件和IItem 接口,所以我们可以使用(在第6行)useState 钩子来创建shoppingList 状态。
我们指定在使用setShoppingList 函数时,钩子应该接受一个IItem 的数组。如果你把鼠标悬停在setShoppingList 函数上,你会看到这个类型。记下它,当我们在AddItem.tsx 中向购物清单添加物品时,我们很快就会需要它了。
回到AddItem.tsx 组件,用这个更新它:
import React, {useState} from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
} from 'react-native';
export interface IItem {
item: string;
quantity: string;
}
interface Props {
setShoppingList: React.Dispatch<React.SetStateAction<IItem[]>>;
shoppingList: IItem[];
}
const AddItem: React.FC<Props> = ({shoppingList, setShoppingList}) => {
const [item, setItem] = useState('');
const [quantity, setQuantity] = useState('');
const addItem = () => {
if (!item) {
Alert.alert('No Item!', 'You need to enter an item');
} else {
setShoppingList([
...shoppingList,
{
item,
quantity: quantity || '1',
},
]);
setItem('');
setQuantity('');
}
};
return (
<View>
<Text style={styles.heading}>Add Shopping Item</Text>
<View style={styles.form}>
<TextInput
style={styles.input}
placeholder="Enter item"
value={item}
onChangeText={text => setItem(text)}
/>
<TextInput
style={styles.input}
placeholder="Enter quantity"
keyboardType="numeric"
value={quantity}
onChangeText={q => {
setQuantity(q);
}}
/>
<TouchableOpacity style={styles.addItemButton} onPress={addItem}>
<Text style={styles.buttonText}>Add Item</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
heading: {
fontSize: 20,
fontWeight: '700',
},
form: {
marginTop: 30,
},
input: {
padding: 15,
borderColor: 'rgba(0, 0, 0, 0.2)',
borderWidth: 1,
borderRadius: 5,
marginBottom: 20,
},
addItemButton: {
backgroundColor: '#eb8634',
paddingVertical: 20,
borderRadius: 5,
alignItems: 'center',
},
buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;
该组件现在有了很大的变化,所以我将带领你完成这些变化。
在第14行,我们定义了Props 接口并设置了每个属性的类型,然后在第18行将其设置为我们的功能组件props 的类型。
我们还在第21行创建了一个处理函数,当 "添加项目 "按钮被点击时,该函数检查是否有项目被输入到字段中,然后调用setShoppingList() 函数,向列表中添加一个新项目。
列出项目
现在我们已经能够添加新的项目,让我们创建一个组件来列出它们。
在/src/components 中创建另一个文件,命名为Item.tsx ,并在里面粘贴下面的代码:
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {IItem} from './AddItem';
const Item: React.FC<IItem> = ({item, quantity}) => {
return (
<View style={styles.item}>
<Text style={styles.itemName}>{item}</Text>
<Text style={styles.quantity}>x{quantity}</Text>
</View>
);
};
const styles = StyleSheet.create({
item: {
padding: 20,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: 'rgba(0, 0, 0, 0.2)',
},
itemName: {
fontWeight: '500',
},
quantity: {
padding: 6,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, 0.2)',
borderRadius: 10,
overflow: 'hidden',
backgroundColor: 'rgba(0, 0, 0, 0.05)',
},
});
export default Item;
你可能已经掌握了它的窍门!Item.tsx 组件接受来自IItem 的属性作为道具,然后我们用一些样式来渲染它们。
现在,我们需要通过将该组件导入App.tsx ,并使用内置的 [FlatList]([https://reactnative.dev/docs/flatlist](https://reactnative.dev/docs/flatlist))组件。
用下面的代码替换App.tsx 中的内容。
import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, View, FlatList} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem';
import Item from './src/components/Item';
const App = () => {
const [shoppingList, setShoppingList] = useState<IItem[]>([]);
return (
<SafeAreaView style={styles.container}>
<Header title="Shopping List" />
<View style={styles.contentWrapper}>
<AddItem
setShoppingList={setShoppingList}
shoppingList={shoppingList}
/>
<FlatList
data={shoppingList}
keyExtractor={(item, index) => `${item.item}-${index}`}
renderItem={({item}) => (
<Item item={item.item} quantity={item.quantity} />
)}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#e8e7e3',
},
contentWrapper: {
padding: 20,
},
});
export default App;
我们的应用程序现在已经完成了。FlatList在第16行,我们使用FlatList 组件来渲染我们列表中的项目,你可以看到我们是如何在renderItem 的道具中使用Item 组件的。
结语
这篇文章旨在简单介绍一下你如何在React Native应用程序中受益于TypeScript的功能。购物清单应用足够简单,向你展示了如何使用TypeScript,而不会让事情变得复杂。
你可以通过实现从列表中删除项目的功能,使事情更进一步。