interface Activity {
eat: () => void
}
interface Person {
activity?: Activity
}
const activity = <T extends Person>(person: T) => ({
eat: person.activity && person.activity.eat
})
const tom = {
activity: {
eat: () => {}
}
}
const tomAct = activity(tom)
tomAct.eat() // should know `eat` does exist
const bobAct = activity({})
bobAct.eat // should know `eat` is undefined
您可以看到tomAct.eat将返回eat: (() => void) | undefined
,但是在这种情况下tomAct
知道eat: (() => void
和bobAct是undefined
。
Typescript是否支持这种情况?我该怎么解决?
===
“打字稿”:“ ^ 3.1.2”,
答案 0 :(得分:1)
Typescript是在编译时上运行的编译器,因此它只能知道当时已知的内容。
您的要求是运行时要求,某些属性的值仅在运行时才知道,因此无法使用TS。
答案 1 :(得分:1)
您的问题是控制流分析在泛型上并不能很好地工作。出于控制流分析的目的,编译器实质上是将T
扩展为Person
(找出person.activity && person.activity.eat
的类型),因此推断出的activity()
返回类型是与该函数的具体(非泛型)版本相同:
const activityConcrete = (person: Person) => ({
eat: person.activity && person.activity.eat
}); // {eat: ()=>void | undefined}
为了获得所需的行为,您要么需要使编译器进行分析(有时是不可能的),要么只需assert即可得到期望的返回类型。传统上,您在这里要做的是使用overloads来表示输入和输出类型之间的关系:
function activity(person: { activity: Activity }): Activity;
function activity(person: { activity?: undefined }): { eat: undefined };
function activity(person: Person): { eat: Activity['eat'] | undefined };
function activity(person: Person): { eat: Activity['eat'] | undefined } {
return {
eat: person.activity && person.activity.eat
}
}
从TypeScript 2.8开始,您可以使用conditional types来表示同一件事:
type PersonEat<T extends Person> = T['activity'] extends infer A ?
A extends Activity ? A['eat'] : undefined : never;
const activity = <T extends Person>(person: T) => ({
eat: person.activity && person.activity.eat
} as { eat: PersonEat<T> })
这两种方法都会导致类似的行为:
const tom = {
activity: {
eat: () => {}
}
}
const bob = {};
const tomAct = activity(tom)
tomAct.eat() // okay
const bobAct = activity(bob)
bobAct.eat // undefined
那行得通。
请注意,在没有Person
的情况下,如何处理activity
会有一点皱纹。上方bob
的类型为{}
,对于对象来说,其类型为top type,这意味着它absorbs与您联合的任何其他对象类型。也就是说,在:
const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}
可以推断出tomOrBob
的类型为{} | {activity: Activity}
,它被折叠为{}
。因此,编译器忘记了tomOrBob
可能有一个activity
。并导致以下错误行为:
const tomOrBobActivity = activity(tomOrBob);
tomOrBobActivity.eat; // undefined but it should be (()=>void) | undefined
如果您对那种过度的不确定性还可以的话,那很好。否则,您需要明确告诉编译器记住activity
中缺少bob
:
const bob: { activity?: undefined } = {}; // bob definitely is missing activity
const bobAct = activity(bob);
bobAct.eat // still undefined as desired
const tomOrBob = Math.random() < 0.5 ? tom : bob;
const tomOrBobAct = activity(tomOrBob);
tomOrBobAct.eat; // (() => void) | undefined
而且表现如预期。
好的,希望能有所帮助。祝你好运!