feat(es/parser): Support extends clause to infer type (#4326)

This commit is contained in:
Pig Fang 2022-04-14 03:16:15 +08:00 committed by GitHub
parent 39dc394933
commit 1c3d1af01c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 4829 additions and 18 deletions

View File

@ -387,6 +387,8 @@ pub struct Context {
allow_direct_super: bool, allow_direct_super: bool,
ignore_else_clause: bool, ignore_else_clause: bool,
disallow_conditional_types: bool,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -898,29 +898,43 @@ impl<I: Tokens> Parser<I> {
let start = cur_pos!(self); let start = cur_pos!(self);
let ty = self.parse_ts_non_conditional_type()?; self.with_ctx(Context {
if self.input.had_line_break_before_cur() || !eat!(self, "extends") { disallow_conditional_types: false,
..self.ctx()
})
.parse_with(|p| {
let ty = p.parse_ts_non_conditional_type()?;
if p.input.had_line_break_before_cur() || !eat!(p, "extends") {
return Ok(ty); return Ok(ty);
} }
let check_type = ty; let check_type = ty;
let extends_type = self.parse_ts_non_conditional_type()?; let extends_type = p.parse_ts_non_conditional_type_within_ctx()?;
expect!(self, '?'); expect!(p, '?');
let true_type = self.parse_ts_type()?; let true_type = p.parse_ts_type()?;
expect!(self, ':'); expect!(p, ':');
let false_type = self.parse_ts_type()?; let false_type = p.parse_ts_type()?;
Ok(Box::new(TsType::TsConditionalType(TsConditionalType { Ok(Box::new(TsType::TsConditionalType(TsConditionalType {
span: span!(self, start), span: span!(p, start),
check_type, check_type,
extends_type, extends_type,
true_type, true_type,
false_type, false_type,
}))) })))
})
}
fn parse_ts_non_conditional_type_within_ctx(&mut self) -> PResult<Box<TsType>> {
self.with_ctx(Context {
disallow_conditional_types: true,
..self.ctx()
})
.parse_ts_non_conditional_type()
} }
/// `tsParseNonConditionalType` /// `tsParseNonConditionalType`
@ -2197,12 +2211,21 @@ impl<I: Tokens> Parser<I> {
let start = cur_pos!(self); let start = cur_pos!(self);
expect!(self, "infer"); expect!(self, "infer");
let type_param_name = self.parse_ident_name()?; let type_param_name = self.parse_ident_name()?;
let constraint = self.try_parse_ts(|p| {
expect!(p, "extends");
let constraint = p.parse_ts_non_conditional_type();
if p.ctx().disallow_conditional_types || !is!(p, '?') {
constraint.map(Some)
} else {
Ok(None)
}
});
let type_param = TsTypeParam { let type_param = TsTypeParam {
span: type_param_name.span(), span: type_param_name.span(),
name: type_param_name, name: type_param_name,
is_in: false, is_in: false,
is_out: false, is_out: false,
constraint: None, constraint,
default: None, default: None,
}; };
Ok(TsInferType { Ok(TsInferType {

View File

@ -0,0 +1,29 @@
type X1<T extends any[]> =
T extends [infer U extends string] ? ["string", U] :
T extends [infer U extends number] ? ["number", U] :
never;
type X2<T extends (...args: any[]) => void> =
T extends (a: infer U extends string) => void ? ["string", U] :
T extends (a: infer U extends number) => void ? ["number", U] :
never;
type X3<T extends (...args: any[]) => any> =
T extends (...args: any[]) => (infer U extends string) ? ["string", U] :
T extends (...args: any[]) => (infer U extends number) ? ["number", U] :
never;
type X4<T extends new (...args: any[]) => any> =
T extends new (...args: any[]) => (infer U extends { a: string }) ? ["string", U] :
T extends new (...args: any[]) => (infer U extends { a: number }) ? ["number", U] :
never;
type X5<T> =
T extends Promise<infer U extends string> ? ["string", U] :
T extends Promise<infer U extends number> ? ["number", U] :
never;
type X6<T> =
T extends { a: infer U extends string } ? ["string", U] :
T extends { a: infer U extends number } ? ["number", U] :
never;

View File

@ -0,0 +1,9 @@
type X10<T> = T extends (infer U extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional
type X11<T> = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional
type X12<T> = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`)
type X13<T> = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type')
type X14<T> = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator)
type X15<T> = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional
type X16<T> = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`)
type X17<T> = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional
type X18<T> = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`)