我有一个异步TS函数,该函数发出一个请求并将响应数据转换为boolean
并返回,但在调用函数VS Code告诉我,当我返回时,返回值为boolean | null
用Promise.all
拨打电话。这是代码:
功能:
import apiAxios from "../apiAxios";
export default async function doesAssignmentHaveTakes(
assignmentId: number
): Promise<boolean> {
const response = await apiAxios.get(`/assignments/${assignmentId}/has-takes`);
return !!response.data;
}
呼叫者:
import React, { FC, useState, useCallback } from "react";
import styled from "styled-components/macro";
import AssignmentForm, {
Props as AssignmentFormProps,
Value as AssignmentFormValue
} from "./AssignmentForm";
import useAsyncEffect from "../utils/useAsyncEffect";
import getAssignmentById from "../api/assignments/getAssignmentById";
import doesAssignmentHaveTakes from "../api/assignmentTakes/doesAssignmentHaveTakes";
interface Props extends AssignmentFormProps {
assignmentId: number;
onSubmit(value: Value): any;
}
export interface Value extends AssignmentFormValue {
assignmentId: number;
}
const EditAssignmentForm: FC<Props> = props => {
const { assignmentId, onSubmit, ...rest } = props;
const [showEditWarning, setShowEditWarning] = useState(false);
const [initialValue, setInitialValue] = useState<AssignmentFormValue | null>(
null
);
useAsyncEffect(
async isCancelled => {
const [fetchedAssignment, hasTakes] = await Promise.all([
getAssignmentById(assignmentId),
doesAssignmentHaveTakes(assignmentId)
]);
if (!fetchedAssignment) {
// TODO: Alert parent component?
return;
}
const value: Value = {
assignmentId: fetchedAssignment.id,
assignment: {
title: fetchedAssignment.title,
subTitle: fetchedAssignment.subTitle
},
sets: fetchedAssignment.sets
.map(set => ({
id: set.id,
index: set.index,
questions: set.questions
.map(question => ({
id: question.id,
index: question.index,
isPractice: question.isPractice,
questionText: question.questionText,
inputType: question.inputType,
questionImage: question.questionImage,
sampleResponseText: question.sampleResponseText,
sampleResponseImage: question.sampleResponseImage
}))
.sort((a, b) => a.index - b.index),
learningTarget: set.learningTarget,
isExampleCorrect: set.isExampleCorrect,
exampleImage: set.exampleImage,
character: set.character
}))
.sort((a, b) => a.index - b.index)
};
if (!isCancelled()) {
setInitialValue(value);
setShowEditWarning(hasTakes);
}
},
[assignmentId]
);
const handleSubmit = useCallback(
(value: AssignmentFormValue) => {
onSubmit({
...value,
assignmentId
});
},
[onSubmit, assignmentId]
);
if (!initialValue) {
// Loading...
return null;
}
return (
<AssignmentForm
{...rest}
initialValue={initialValue}
onSubmit={handleSubmit}
/>
);
};
export default styled(EditAssignmentForm)``;
问题的具体内容:
const [fetchedAssignment, hasTakes] = await Promise.all([
getAssignmentById(assignmentId),
doesAssignmentHaveTakes(assignmentId)
]);
和
setShowEditWarning(hasTakes);
TS错误:
TypeScript error in /Users/james/projects/math-by-example/client/src/components/EditAssignmentForm.tsx(71,28):
Argument of type 'boolean | null' is not assignable to parameter of type 'SetStateAction<boolean>'.
Type 'null' is not assignable to type 'SetStateAction<boolean>'. TS2345
69 | if (!isCancelled()) {
70 | setInitialValue(value);
> 71 | setShowEditWarning(hasTakes);
| ^
72 | }
73 | },
74 | [assignmentId]
以及VS Code中的错误的一些屏幕截图
TS为什么将null
添加到已解析的Promise.all
类型中?
答案 0 :(得分:7)
解决方案是将as const
添加到传递给Promise.all
的数组中。
问题不在于键入Promise.all
还是编译器中的错误。问题是TypeScript在默认情况下对数组执行的操作。考虑一下:
const q = [1, "a"];
q
的默认类型推断为(string | number)[]
。即使您将数字作为第一个位置,将字符串作为第二个位置,TypeScript也会推断 all 位置可以是字符串或数字。如果希望TypeScript将数组视为元组并为每个位置分配尽可能窄的类型,则可以执行以下操作:
const q = [1, "a"] as const;
TS将为此数组推断readonly [1, "a"]
的类型。因此q
在第一个位置只能有数字1,在第二个位置只能有字符串"a"
。 (它也是只读的,但这是一个附带问题。)这是在TypeScript 3.4中引入的。
好的,这与您的案件有什么关系?当您将数组传递给Promise.all
时,TypeScript使用的是我在第一个示例中显示的类型推断。 Promise.all
看到一个数组,其中每个项目都可以采用该项目可以采用的所有值的并集。如果使用as const
,则推论将类似于我上面显示的第二种情况,这将相应地反映在Promise.all
的类型中。同样,Promise.all
的键入没有没问题。它可以满足以下条件:输入错误,输入错误。
这是一个插图(也在playground中):
async function fa(): Promise<string | null> { return "foo"; }
async function fb(): Promise<boolean> { return true; }
async function main(): Promise<void> {
let a: string | null;
let b: boolean;
// Remove this "as const" and the code won't compile.
[a, b] = await Promise.all([fa(), fb()] as const);
console.log(a, b);
}
main();
答案 1 :(得分:2)
此问题已从ts 3.9+(release note)解决,升级到3.9,您将不会看到此错误。
答案 2 :(得分:0)
问题在于Promise.all
,take a look的类型定义。 .all
的返回类型总是尝试使该数组的并集产生通用类型。
其他函数getAssignmentById
可能返回null
,因此Promise.all
会推断出返回类型为[something | null, boolean | null]
。我不确定TS编译器可能有错误。我在相同的条件下制作了一个playground,以查看实践中的推论,看看它如何推导Promise构造函数上的泛型类型,然后从{{1}的返回类型中删除null
},然后再次查看funcB
类型...它的行为符合预期。
答案 3 :(得分:0)
同意佩德罗的回答,即Promise.all不会开箱即用地处理不同的退货类型。
您可以尝试这样声明Promise的返回类型:
const [fetchedAssignment, hasTakes] = await Promise.all<string | null, boolean>([
getAssignmentById(assignmentId),
doesAssignmentHaveTakes(assignmentId)
]);