十二、答案
第 1 章:TypeScript 基础
-
五种基本类型是什么?
-
string
:表示一个 Unicode 字符序列 number
:表示整数和浮点数boolean
:表示逻辑正确或错误undefined
:表示一个尚未初始化的值-
null
:表示无值 -
以下代码中的
flag
变量的推断类型是什么?
const flag = false;
flag
将被推断为boolean
类型。
- 接口和类型别名之间有什么区别
主要区别在于类型别名不能从扩展或实现,就像使用接口一样。
- 以下代码有什么问题?
class Product {
constructor(public name: string, public unitPrice: number) {}
}
let table = new Product();
table.name = "Table";
table.unitPrice = 700;
构造函数要求通过name
和unitPrice
。这里有两种解决问题的方法。
在构造函数中传递值:
let table = new Product("Table", 700);
将参数设置为可选:
class Product {
constructor(public name?: string, public unitPrice?: number) {}
}
- 如果我们希望 TypeScript 程序支持 IE11,
--target
编译器选项应该是什么?
这应该是es5
,因为 IE11 最多只支持 ES5 功能。
- 是否可以让 TypeScript 编译器传输 ES6 JavaScript 文件?如果是,怎么做?
对我们可以使用--allowJS
设置让编译器传输 JavaScript 文件
- 我们如何防止
console.log()
语句进入我们的代码****
****我们可以使用 tslint 和"no-console"
规则来强制执行这一点。这将是tslint.json
中的规则:
{
"rules": {
"no-console": true
}
}
第 2 章:TypeScript 3 的新增功能
- 我们有以下绘制点的函数:
function drawPoint(x: number, y: number, z: number) {
...
}
我们还有以下point
变量:
const point: [number, number, number] = [100, 200, 300];
我们如何以简洁的方式调用drawPoint
函数?
drawPoint(...point);
- 我们需要创建另一个版本的
drawPoint
函数,该函数可以通过传递 x、y 和 z 点值作为参数来调用:
drawPoint(1, 2, 3);
在drawPoint
的内部实现中,我们从元组数据类型[number, number, number]
中提取点。我们如何使用所需的元组定义方法参数?
function drawPoint(...point: [number, number, number]) {
...
}
- 在您的
drawPoint
实现中,您如何使z
点成为可选的?
function drawPoint(...point: [number, number, number?]) {
...
}
- 我们有一个名为
getData
的函数,它调用 web API 来获取一些数据。不同的 API 资源的数量还在增长,所以我们选择使用any
作为返回类型:
function getData(resource: string): any {
const data = ... // call the web API
if (resource === "person") {
data.fullName = `${data.firstName} ${data.surname}`;
}
return data;
}
我们如何利用unknown
类型使getData
类型更安全?
class Person {
firstName: string;
surname: string;
fullName: string;
}
function getData(resource: string): unknown {
const data = {};
if (data instanceof Person) {
data.fullName = `${data.firstName} ${data.surname}`;
}
return data;
}
- 我们可以使用什么
build
标志来确定哪些项目已经过时,需要在不进行重建的情况下重建?
tsc --build ... --dry --verbose
第 3 章:React 和 TypeScript 入门
- 在开发过程中,允许调试器语句和登录控制台的
TSLint
设置是什么?
"rules": {
"no-debugger": false,
"no-console": false,
},
- 在 JSX 中,我们如何在类组件中显示带有名为
buttonLabel
的道具标签的按钮?
<button>{this.props.buttonLabel}</button>
- 我们如何使
buttonLabel
道具成为可选的,并默认这样做?
道具界面类型标注前使用?
:
interface IProps {
buttonLabel?: string
}
在类组件的顶部实现一个静态defaultProps
对象:
public static defaultProps = {
buttonLabel: "Do it"
};
- 在 JSX 中,如果称为
doItVisible
的状态为 true,我们如何才能显示前面的按钮?假设我们已经声明了一个包含doItVisible
的状态类型,并且它已经在构造函数中初始化
{this.state.doItVisible && <button>{this.props.buttonLabel}</button>}
- 我们如何为此按钮创建单击处理程序?
<button onClick={this.handleDoItClick}>{this.props.buttonLabel}</button>
private handleDoItClick = () => {
// TODO: some stuff!
};
- 我们声明了一个包含
doItDisabled
的状态类型。它也已在构造函数中初始化。我们将如何设置此状态以在单击后禁用“执行”按钮?
private handleDoItClick = () => {
this.setState({doItDisabled: true})
};
<button disabled={this.state.doItDisabled}>{this.props.buttonLabel}</button>
- 如果按钮在禁用时被单击,是否仍会调用单击处理程序?
不
- 在类组件中使用哪个生命周期方法将事件处理程序添加到存在于 React 组件中的 none React web 组件?
componentDidMount
- 然后,我们将使用哪个生命周期方法删除此事件处理程序?
componentWillUnmount
- 我们有一个名为
Counter
的功能组件。它需要包含一个名为count
的状态和一个名为setCount
的更新函数。如何定义此状态并将初始计数默认为 10?
const count, setCount = React.useState(10);
- 在前面的
Counter
组件中,我们有一个decrement
函数,需要将count
减少 1:
const decrement = () => {
// TODO - reduce count by 1
};
如何实现这一点?
const decrement = () => {
setCount(count - 1);
};
第 4 章:使用 React 路由的路由
- 我们有以下
Route
显示客户名单:
<Route path="/customers" component={CustomersPage} />
当页面为"/customers"
时,CustomersPage
组件会呈现吗?
对
- 当页面为
"/customers/24322"
时,CustomersPage
组件会呈现吗? 对 - 我们只希望在路径为
"/customers"
时渲染CustomersPage
组件。我们如何更改Route
上的属性来实现这一点?
我们可以使用exact
属性:
<Route exact={true} path="/customers" component={CustomersPage} />
- 可以处理路径
"/customers/24322"
的Route
是什么?它应该将"24322"
放入一个名为customerId
的路由参数中:
<Route exact={true} path="/customers/:customerId" component={CustomerPage} />
- 然后我们可以使用
RouteComponentProps
作为CustomerPage
道具类型,通过props.match.params.``customerId
访问customerId
。
我们如何捕捉不存在的路径以便通知用户?
确保所有的Route
组件都包裹在Switch
组件中。然后,我们可以向组件添加一个Route
,该组件向用户呈现一条未找到的消息,作为开关中的最后一个Route
:
<Switch>
<Route path="/customers/:customerId" component={CustomerPage} />
<Route exact={true} path="/customers" component={CustomersPage} />
<Route component={NotFoundPage} />
</Switch>
- 我们如何在
CustomersPage
中实现search
查询参数?因此,"/customers/?search=Cool Company"
会向客户展示酷公司的名称。
首先,我们需要在我们的课堂上使用RouteComponentProps
类型的道具:
import { RouteComponentProps } from "react-router-dom";
class CustomersPage extends React.Component<RouteComponentProps, IState> { ... }
我们可以使用URLSearchParams
获取search
查询参数,并在componentDidMount
生命周期方法中进行搜索:
public componentDidMount() {
const searchParams = new URLSearchParams(props.location.search);
const search = searchParams.get("search") || "";
const products = await ... // make web service call to do search
this.setState({ products });
}
- 过了一会儿,我们决定将
"customer"
路径更改为"clients"
。我们如何实现这一点,以便用户仍然可以使用现有的"customer"
路径,但让路径自动重定向到新的"client"
路径。
我们可以使用Redirect
组件将旧路径重定向到新路径:
<Switch>
<Route path="/clients/:customerId" component={CustomerPage} />
<Route exact={true} path="/clients" component={CustomersPage} />
<Redirect from="/customers/:customerId" to="/clients/:customerId" />
<Redirect exact={true} from="/customers" to="/clients" />
<Route component={NotFoundPage} />
</Switch>
第五章:高级类型
- 我们有一个表示课程结果的界面,如下所示:
interface ICourseMark {
courseName: string;
grade: string;
}
我们可以按如下方式使用此界面:
const geography: ICourseMark = {
courseName: "Geography",
grade: "B"
}
等级只能是 A、B、C 或 D。我们如何在此界面中创建更强类型的grade
属性?
我们可以使用联合类型:
interface ICourseMark {
courseName: string;
grade: "A" | "B" | "C" | "D";
}
- 我们有以下函数,用于验证数字和字符串是否填充了值:
function isNumberPopulated(field: number): boolean {
return field !== null && field !== undefined;
}
function isStringPopulated(field: string): boolean {
return field !== null && field !== undefined && field !== "";
}
我们如何将这些组合成一个名为isPopulated
的函数,并使用签名重载?
我们可以使用重载签名,然后在主函数中为field
使用联合类型。然后我们可以在函数中使用typeof
类型的 guard 来处理不同的逻辑分支:
function isPopulated(field: number): boolean
function isPopulated(field: string): boolean
function isPopulated(field: number | string): boolean {
if (typeof field === "number") {
return field !== null && field !== undefined;
} else {
return field !== null && field !== undefined && field !== "";
}
}
- 我们如何用泛型实现更灵活的
isPopulated
函数版本?
对于字符串的特殊代码分支,我们可以使用带有typeof
类型保护的泛型函数:
function isPopulated<T>(field: T): boolean {
if (typeof field === "string") {
return field !== null && field !== undefined && field !== "";
} else {
return field !== null && field !== undefined;
}
}
- 我们有以下类型的阶段别名:
type Stages = {
pending: 'Pending',
started: 'Started',
completed: 'Completed',
};
我们如何通过编程将其转换为 union 类型'Pending' | 'Started' | 'Completed'
?
我们可以使用keyof
关键字:
type StageUnion = keyof Stages
- 我们有以下联合类型:
type Grade = 'gold' | 'silver' | 'bronze';
我们如何以编程方式创建以下类型?
type GradeMap = {
gold: string;
silver: string;
bronze: string
};
我们可以按如下方式映射类型:
type GradeMap = { [P in Grade]: string }
第 6 章:组件模式
- React 为我们访问子组件提供了什么特殊属性?
一个名为children
的属性
- 有多少组件可以与 React 上下文共享状态?
组件层次结构中的提供者组件下有我们喜欢的任意多个组件
- 在使用 React 上下文时,它使用什么模式允许我们使用上下文呈现内容?
渲染道具模式
- 一个组件中可以有多少渲染道具?
我们喜欢多少就有多少
- 一个组件中有多少儿童道具?
1.
- 我们只在产品页面上使用了
withLoader
。我们在ProductData.ts
中有以下功能获取所有产品:
export const getProducts = async (): Promise<IProduct[]> => {
await wait(1000);
return products;
};
您可以通过使用withLoader
HOC 在产品页面上实现加载器微调器吗。
首先,我们将ProductPage
拆分为一个容器和表示组件。呈现组件将呈现产品列表,并将其包装在withLoader
HOC 中:
import * as React from "react";
import { Link } from "react-router-dom";
import { IProduct } from "./ProductsData";
import withLoader from "./withLoader";
interface IProps {
products: IProduct[];
search: string;
}
const ProductList: React.SFC<IProps> = props => {
const { products, search } = props;
return (
<ul className="product-list">
{products.map(product => {
if (
!search ||
(search &&
product.name.toLowerCase().indexOf(search.toLowerCase()) > -1)
) {
return (
<li key={product.id} className="product-list-item">
<Link to={`/products/${product.id}`}>{product.name}</Link>
</li>
);
} else {
return null;
}
})}
</ul>
);
};
export default withLoader(ProductList);
然后我们可以在ProductPage
中使用它,其render
方法如下:
public render() {
return (
<div className="page-container">
<p>
Welcome to React Shop where you can get all your tools for ReactJS!
</p>
<ProductList
loading={this.state.loading}
products={this.state.products}
search={this.state.search}
/>
</div>
);
}
- 可以使用儿童道具模式创建加载器旋转器吗?因此,消费 JSX 应该是这样的:
<Loader loading={this.state.loading}>
<div>
The content for my component ...
</div>
</Loader>
如果是的话,试一试吧?
对
import * as React from "react";
interface IProps {
loading: boolean;
}
const Loader: React.SFC<IProps> = props =>
props.loading ? (
<div className="loader-overlay">
<div className="loader-circle-wrap">
<div className="loader-circle" />
</div>
</div>
) : props.children ? (
<React.Fragment>{props.children}</React.Fragment>
) : null;
export default Loader;
第 7 章:使用表单
-
扩展我们的通用
Field
组件,以包括使用本机数字输入的数字编辑器。 -
首先,在
type
道具中增加"Number"
:
interface IFieldProps {
...
type?: "Text" | "Email" | "Select" | "TextArea" | "Number";
}
- 在呈现
input
时包括"Number"
类型:
{(type === "Text" || type === "Email" || type === "Number") && (
<input
type={type.toLowerCase()}
id={name}
value={context.values[name]}
onChange={e => handleChange(e, context)}
onBlur={e => handleBlur(e, context)}
/>
)}
- 在“联系我们”表单上设置一个紧急字段,以指示响应的紧急程度。该字段应为数字。
在注释字段之后立即添加以下字段:
<Form.Field name="urgency" label="How urgent is a response?" type="Number" />
- 在泛型
Form
组件中实现一个新的验证器函数,用于验证一个数字是否介于其他两个数字之间。
在Form.tsx
中增加以下功能:
export const between: Validator = (
fieldName: string,
values: IValues,
bounds: { lower: number; upper: number }
): string =>
values[fieldName] &&
(values[fieldName] < bounds.lower || values[fieldName] > bounds.upper)
? `This must be between ${bounds.lower} and ${bounds.upper}`
: "";
-
在紧急字段上实施验证规则,以确保其介于 1 和 10 之间。
-
首先将
between
验证器导入ContactUs.tsx
:
import { between, Form, ISubmitResult, IValues, minLength, required } from "./Form";
- 在
ContactUs.tsx
的validationRules
道具中增加紧急规则:
validationRules={{
email: { validator: required },
name: [{ validator: required }, { validator: minLength, arg: 3 }],
urgency: [{ validator: between, arg: { lower: 1, upper: 10 } }]
}}
-
当用户在没有键入任何内容的情况下点击一个字段时,就会触发我们的验证。当一个字段失去焦点但仅当它被更改时,我们如何触发验证?试着实现一下。
-
我们需要跟踪一个字段是否在表单状态下被触摸:
interface ITouched {
[key: string]: boolean;
}
interface IState {
touched: ITouched;
...
}
- 我们在构造函数中将每个字段的
touched
值初始化为false
:
constructor(props: IFormProps) {
super(props);
const errors = {};
const touched = {};
Object.keys(props.defaultValues).forEach(fieldName => {
errors[fieldName] = [];
touched[fieldName] = false;
});
this.state = {
errors,
submitted: false,
submitting: false,
touched,
values: props.defaultValues
};
}
- 在
setValue
方法中,我们将被更新字段的touched
值更新为true
:
private setValue = (fieldName: string, value: any) => {
const newValues = { ...this.state.values, [fieldName]:
value };
const newTouched = { ...this.state.touched, [fieldName]:
true };
this.setState({ values: newValues, touched: newTouched });
};
- 在 validate 方法的顶部,我们检查字段是否已被触摸,如果未被触摸,我们将返回一个空数组以指示字段有效:
private validate = (fieldName: string, value: any): string[] => {
if (!this.state.touched[fieldName]) {
return [];
}
...
};
第 8 章:反应重复
- 是否需要
type
action 对象中的属性?这个属性需要被称为type
吗?我们可以叫它别的吗?
type
属性在 action 对象中是必需的,必须调用type
。
- action 对象可以包含多少属性?
我们喜欢多少就有多少!对于type
属性,它需要至少包含一个。然后,它可以包括我们需要的许多其他属性,以便减速器更改状态,但这通常集中在一个附加属性中。因此,通常一个动作会有一个或两个属性。
- 什么是动作创造者?
动作创建者是返回动作对象的函数。组件调用这些函数以更改存储中的状态。
- 为什么我们在 React shop 应用程序的 Redux 商店中需要 Redux Thunk?
默认情况下,Redux 存储无法管理异步操作创建者。需要将中间件添加到 Redux 存储中,以方便异步操作创建者。Redux Thunk 是我们为此添加的中间件。
- 我们能不能用些别的东西,而不是 Redux Thunk?
对我们本可以创建自己的中间件。我们也可以使用其他成熟的库,比如 Redux Saga。
- 在我们刚刚实现的
basketReducer
中,为什么不使用push
函数将项目添加到篮子状态?突出显示的行有什么问题?
export const basketReducer: Reducer<IBasketState, BasketActions> = (
state = initialBasketState,
action
) => {
switch (action.type) {
case BasketActionTypes.ADD: {
state.products.push(action.product);
}
}
return state || initialBasketState;
};
这直接改变了产品的状态,使功能不纯。这是因为我们改变了 state 参数,它超出了我们函数的范围。在本例中,如果违反此规则,则在单击“添加到篮子”按钮时,呈现页面上的篮子摘要不会增加。
第 9 章:与 RESTful API 的交互
- 如果在浏览器中运行以下代码,控制台中的输出将是什么?
try {
setInterval(() => {
throw new Error("Oops");
}, 1000);
} catch (ex) {
console.log("Sorry, there is a problem", ex);
}
我们会收到一条消息说发生了未捕获错误(Oops)。The console.log
语句无法到达。
- 假设 9999 后不存在,如果我们在浏览器中运行以下代码,控制台中的输出会是什么:
fetch("https://jsonplaceholder.typicode.com/posts/9999")
.then(response => {
console.log("HTTP status code", response.status);
return response.json();
})
.then(data => console.log("Response body", data))
.catch (error => console.log("Error", error));
关键的一点是,HTTP 错误不会在带有fetch
函数的catch
方法中得到处理。
- 如果我们对
axios
做了类似的练习,那么在运行以下代码时控制台中的输出是什么?
axios
.get("https://jsonplaceholder.typicode.com/posts/9999")
.then(response => {
console.log("HTTP status code", response.status);
})
.catch(error => {
console.log("Error", error.response.status);
});
关键的一点是,HTTP 错误确实会在带有axios
的catch
方法中得到处理。
- 与
axios
相比,使用本机fetch
有什么好处?
如果我们的目标是现代浏览器(而不是 IE),并且只需要简单的 REST API 交互,那么fetch
可以说比axios
更有利,因为我们的代码不依赖于第三方代码。由于执行的非本机代码较少,它也可能会运行得更快一些。
- 我们如何向以下
axios
请求添加承载令牌?
axios.get("https://jsonplaceholder.typicode.com/posts/1")
- 第二个参数是一个对象文字,它有一个可以包含请求 HTTP 头的
header
属性:
axios
.get("https://jsonplaceholder.typicode.com/posts/1", {
headers: {
"Authorization": `Bearer ${token}`
}
});
- 我们正在使用以下
axios``PUT
请求更新帖子标题:
axios.put("https://jsonplaceholder.typicode.com/posts/1", {
title: "corrected title",
body: "some stuff"
});
身体没有改变,只是我们想要更新的标题。我们如何才能将其更改为PATCH
请求,以使这个 REST 呼叫更有效?
axios.patch("https://jsonplaceholder.typicode.com/posts/1", {
title: "corrected title"
});
- 我们已经实现了一个功能组件来显示帖子。它使用以下代码从 REST API 获取 post:
React.useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(...)
.catch(...);
});
前面的代码有什么问题?
useEffect
函数中的第二个参数丢失,这意味着每次呈现组件时都会调用 REST API。应提供空数组作为第二个参数,以便仅在第一次渲染时调用 REST API:
React.useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(...)
.catch(...);
}, []);
第 10 章:与 GraphQLAPI 的交互
- 在 GitHub GraphQL 资源管理器中,创建一个查询以返回 React 项目中最后五个打开的问题。在响应中返回问题标题和 URL:
query {
repository (owner:"facebook", name:"react") {
issues(last: 5, states:[OPEN]) {
edges {
node {
title
url
}
}
}
}
}
- 增强最后一个查询,将返回的问题数设置为一个参数,并将此默认值设置为 5:
query ($lastCount: Int = 5) {
repository (owner:"facebook", name:"react") {
issues(last: $lastCount, states: [OPEN]) {
edges {
node {
title
url
}
}
}
}
}
- 在 GitHub GraphQL 资源管理器中创建一个变体,以取消星型存储库的启动。变异应将存储库 ID 作为参数:
mutation ($repoId: ID!) {
removeStar(input: { starrableId: $repoId }) {
starrable {
stargazers {
totalCount
}
}
}
}
- GraphQL 查询进入 HTTP 请求的哪一部分?
HTTP 主体
- GraphQL 变异进入 HTTP 请求的哪一部分?
HTTP 主体
- 如何使
react-apollo``Query
组件类型的响应安全?
创建另一个扩展Query
的组件,将结果的类型作为泛型参数传入:
class MyQuery extends Query<IResult> {}
然后我们可以在 JSX 中使用MyQuery
组件。
- 当您使用
react-boost
构建项目时,默认情况下是启用还是禁用缓存?
在…上
- 我们可以在
Mutation
组件上使用什么道具来更新本地缓存?
update
道具。
第 11 章:使用 Jest 的单元测试
- 假设我们正在实现一个 Jest 测试,我们有一个名为
result
的变量,我们要检查它不是null
。我们如何使用 Jest matcher 函数实现这一点?
expect(result).not.toBeNull()
- 假设我们有一个名为
person
的变量,类型为IPerson
:
interface IPerson {
id: number;
name: string;
}
我们要检查person
变量是否为{ id: 1, name: "bob" }
。我们如何使用 Jest matcher 函数实现这一点?
expect(person).not.toBeEqual({ id: 1, name: "bob" });
- 是否可以使用 Jest 快照测试执行最后一个问题中的检查?如果是,怎么做?
对:
expect(person).toMatchSnapshot();
- 我们已经实现了一个名为
CheckList
的组件,它从列表中的数组中呈现文本。每个列表项都有一个复选框,以便用户可以选择列表项。该组件有一个名为onItemSelect
的函数 prop,当用户选中复选框选择一个项目时调用该函数 prop。我们正在进行一项测试,以验证onItemSelect
道具是否有效。以下代码行在测试中呈现组件:
const { container } = render(<SimpleList data={["Apple", "Banana", "Strawberry"]} onItemSelect={handleListItemSelect} />);
我们如何为handleListItemSelect
使用 Jest 模拟函数并检查它是否被调用?
const handleListItemSelect = jest.fn();
const { container } = render(<SimpleList data={["Apple", "Banana", "Strawberry"]} onItemSelect={handleListItemSelect} />);
// TODO - select the list item
expect(handleListItemSelect).toBeCalledTimes(1);
- 在上一个问题
SimpleList
的实现中,onItemSelect
函数引入了一个名为item
的参数,该参数是用户选择的string
值。在我们的测试中,假设我们已经模拟了用户选择"Banana"
。如何检查调用了onItemSelect
函数,而项目参数为"Banana"
?
expect(handleListItemSelect).toBeCalledWith("Banana");
- 在最后两个问题的
SimpleList
实现中,文本使用一个标签显示,该标签与复选框绑定,并使用for
属性。我们如何使用 React 测试库中的函数首先定位"Banana"
复选框,然后再进行检查?
const checkbox = getByLabelText("Banana") as HTMLInputElement;
fireEvent.change(checkbox, {
target: { checked: true }
});
- 在本章中,我们发现呈现来自 JSONPlaceholder REST API 的帖子的代码覆盖率很低。当我们从 RESTAPI 获取帖子时,
componentDidMount
函数中处理 HTTP 错误代码是其中一个未涉及的领域。创建一个测试以覆盖此代码区域:
test("When the post GET request errors when the page is loaded, an error is shown", async () => {
const mock = new MockAdapter(axios);
mock.onGet("https://jsonplaceholder.typicode.com/posts").reply(404);
const { getByTestId } = render(<App />);
const error: any = await waitForElement(() => getByTestId("error"));
expect(error).toMatchSnapshot();
});
需要在App
组件代码中添加测试 ID:
jsx
{this.state.error && <p className="error" data-testid="error">{this.state.error}</p>}
****
版权属于:月萌API www.moonapi.com,转载请注明出处