diff --git a/crates/stc_ts_file_analyzer/src/analyzer/assign/mod.rs b/crates/stc_ts_file_analyzer/src/analyzer/assign/mod.rs index 965cdac254..f921ceca3b 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/assign/mod.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/assign/mod.rs @@ -270,14 +270,11 @@ impl Analyzer<'_, '_> { let mut skip_check_null_or_undefined_of_rhs = false; match op { op!("*=") | op!("**=") | op!("/=") | op!("%=") | op!("-=") => { - if let Type::Keyword(KeywordType { - kind: TsKeywordTypeKind::TsUndefinedKeyword | TsKeywordTypeKind::TsNullKeyword, - .. - }) = rhs - { + if rhs.is_null_or_undefined() { if op == op!("**=") { skip_check_null_or_undefined_of_rhs = true; } + if op != op!("**=") && !self.rule().strict_null_checks && (l.is_num() || l.is_enum_variant()) { skip_check_null_or_undefined_of_rhs = true; } else { diff --git a/crates/stc_ts_file_analyzer/src/analyzer/assign/type_el.rs b/crates/stc_ts_file_analyzer/src/analyzer/assign/type_el.rs index f9c29af448..8638417187 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/assign/type_el.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/assign/type_el.rs @@ -1413,15 +1413,7 @@ impl Analyzer<'_, '_> { l_index_ret_ty, &Type::new_union( span, - vec![ - *r_prop_ty.clone(), - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - ], + vec![*r_prop_ty.clone(), Type::undefined(span, Default::default())], ), opts, ) diff --git a/crates/stc_ts_file_analyzer/src/analyzer/class/mod.rs b/crates/stc_ts_file_analyzer/src/analyzer/class/mod.rs index d4c8517fbd..51e315d364 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/class/mod.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/class/mod.rs @@ -80,12 +80,7 @@ impl Analyzer<'_, '_> { .assign_with_opts( &mut Default::default(), ty, - &Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), + &Type::undefined(span, Default::default()), AssignOpts { span, ..Default::default() diff --git a/crates/stc_ts_file_analyzer/src/analyzer/expr/array.rs b/crates/stc_ts_file_analyzer/src/analyzer/expr/array.rs index 53d4585e3f..c7c2332a62 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/expr/array.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/expr/array.rs @@ -299,12 +299,7 @@ impl Analyzer<'_, '_> { if !errors.is_empty() { if can_use_undefined && errors.len() != u.types.len() { - types.push(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + types.push(Type::undefined(span, Default::default())); types.dedup_type(); return Ok(Cow::Owned(Type::new_union(span, types))); } diff --git a/crates/stc_ts_file_analyzer/src/analyzer/expr/bin.rs b/crates/stc_ts_file_analyzer/src/analyzer/expr/bin.rs index 42b980d736..ed7f2357ec 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/expr/bin.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/expr/bin.rs @@ -426,20 +426,7 @@ impl Analyzer<'_, '_> { }; let exclude_types = if is_loose_comparison_with_null_or_undefined { - vec![ - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - ] + Type::null_and_undefined(span, Default::default()) } else { exclude.freezed() }; @@ -1874,16 +1861,7 @@ impl Analyzer<'_, '_> { let actual = name.slice_to(name.len() - 1); if has_undefined && candidates.is_empty() { - return Ok(( - actual, - Type::Keyword(KeywordType { - span: u.span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - excluded.freezed(), - )); + return Ok((actual, Type::undefined(span, Default::default()), excluded.freezed())); } let ty = Type::new_union(span, candidates).freezed(); return Ok((actual, ty, excluded.freezed())); @@ -2280,20 +2258,7 @@ impl Analyzer<'_, '_> { temp_vec.clone() } else { if is_loose_comparison { - vec![ - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - ] + Type::null_and_undefined(span, Default::default()) } else { vec![] } @@ -2344,20 +2309,7 @@ impl Analyzer<'_, '_> { temp_vec.clone() } else { if is_loose_comparison { - vec![ - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - ] + Type::null_and_undefined(span, Default::default()) } else { vec![] } diff --git a/crates/stc_ts_file_analyzer/src/analyzer/expr/mod.rs b/crates/stc_ts_file_analyzer/src/analyzer/expr/mod.rs index ef72faaba3..cdf9095880 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/expr/mod.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/expr/mod.rs @@ -2612,12 +2612,7 @@ impl Analyzer<'_, '_> { } if !opts.disallow_inexact && metadata.inexact { - return Ok(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + return Ok(Type::undefined(span, Default::default())); } return Err(ErrorKind::NoSuchProperty { @@ -2720,12 +2715,7 @@ impl Analyzer<'_, '_> { } else { if !errors.is_empty() { if is_all_tuple && errors.len() != types.len() { - result_types.push(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + result_types.push(Type::undefined(span, Default::default())); result_types.dedup_type(); let ty = Type::new_union(span, result_types); ty.assert_valid(); @@ -2828,13 +2818,9 @@ impl Analyzer<'_, '_> { if v as usize >= elems.len() { if opts.use_undefined_for_tuple_index_error { - return Ok(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + return Ok(Type::undefined(span, Default::default())); } + if opts.use_last_element_for_tuple_on_out_of_bound { return Ok(*elems.last().unwrap().ty.clone()); } @@ -2848,12 +2834,7 @@ impl Analyzer<'_, '_> { } .context("returning undefined because it's l-value context"), ); - return Ok(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + return Ok(Type::undefined(span, Default::default())); } return Err(ErrorKind::TupleIndexError { @@ -4783,13 +4764,9 @@ impl Analyzer<'_, '_> { type_ann: Option<&Type>, ) -> VResult { if i.sym == js_word!("undefined") { - return Ok(Type::Keyword(KeywordType { - span: i.span.with_ctxt(SyntaxContext::empty()), - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + return Ok(Type::undefined(i.span.with_ctxt(SyntaxContext::empty()), Default::default())); } + let ty = self.type_of_var(i, mode, type_args)?; if self.ctx.should_store_truthy_for_access && mode == TypeOfMode::RValue { // `i` is truthy @@ -4840,20 +4817,10 @@ impl Analyzer<'_, '_> { RLit::Null(RNull { span }) => { if self.ctx.in_export_default_expr { // TODO(kdy1): strict mode - return Ok(Type::Keyword(KeywordType { - span: *span, - kind: TsKeywordTypeKind::TsAnyKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + return Ok(Type::any(*span, Default::default())); } - Ok(Type::Keyword(KeywordType { - span: *span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - })) + Ok(Type::null(*span, Default::default())) } RLit::Regex(v) => Ok(Type::Ref(Ref { span: v.span, diff --git a/crates/stc_ts_file_analyzer/src/analyzer/props.rs b/crates/stc_ts_file_analyzer/src/analyzer/props.rs index 148e77e819..1e60d6fc26 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/props.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/props.rs @@ -300,11 +300,10 @@ impl Analyzer<'_, '_> { false } - Type::Union(u) => u.types.iter().all(|ty| { - ty.is_kwd(TsKeywordTypeKind::TsNullKeyword) - || ty.is_kwd(TsKeywordTypeKind::TsUndefinedKeyword) - || self.is_type_valid_for_computed_key(span, ty) - }), + Type::Union(u) => u + .types + .iter() + .all(|ty| ty.is_null_or_undefined() || self.is_type_valid_for_computed_key(span, ty)), _ => false, } diff --git a/crates/stc_ts_file_analyzer/src/analyzer/stmt/return_type.rs b/crates/stc_ts_file_analyzer/src/analyzer/stmt/return_type.rs index 7c25c765e0..bf50e4418e 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/stmt/return_type.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/stmt/return_type.rs @@ -563,12 +563,10 @@ impl Analyzer<'_, '_> { self.scope.return_values.yield_types.push(item_ty); } else { - self.scope.return_values.yield_types.push(Type::Keyword(KeywordType { - span: e.span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - })); + self.scope + .return_values + .yield_types + .push(Type::undefined(e.span, Default::default())); } Ok(Type::any(e.span, Default::default())) diff --git a/crates/stc_ts_file_analyzer/src/analyzer/types/mod.rs b/crates/stc_ts_file_analyzer/src/analyzer/types/mod.rs index 851ec77592..0e41f2fc70 100644 --- a/crates/stc_ts_file_analyzer/src/analyzer/types/mod.rs +++ b/crates/stc_ts_file_analyzer/src/analyzer/types/mod.rs @@ -929,12 +929,7 @@ impl Analyzer<'_, '_> { if sum >= 2 { if sum == 2 && is_undefined && is_void { - return Ok(Some(Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: KeywordTypeMetadata { ..Default::default() }, - tracker: Default::default(), - }))); + return Ok(Some(Type::undefined(span, Default::default()))); } return never!(); } @@ -2498,18 +2493,8 @@ impl Analyzer<'_, '_> { } let mut unknown = vec![ - Type::Keyword(KeywordType { - span: *span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), - Type::Keyword(KeywordType { - span: *span, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), + Type::null(*span, Default::default()), + Type::undefined(*span, Default::default()), Type::TypeLit(TypeLit { span: *span, members: vec![], diff --git a/crates/stc_ts_file_analyzer/src/ty/type_facts.rs b/crates/stc_ts_file_analyzer/src/ty/type_facts.rs index b64aeaebf4..e9c29ddeae 100644 --- a/crates/stc_ts_file_analyzer/src/ty/type_facts.rs +++ b/crates/stc_ts_file_analyzer/src/ty/type_facts.rs @@ -127,18 +127,13 @@ impl Analyzer<'_, '_> { Type::new_union_without_dedup( span, vec![ + Type::null(span, Default::default()), Type::Keyword(KeywordType { kind: TsKeywordTypeKind::TsObjectKeyword, metadata: Default::default(), span, tracker: Default::default(), }), - Type::Keyword(KeywordType { - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - span, - tracker: Default::default(), - }), ], ), ], @@ -467,25 +462,18 @@ impl Fold for TypeFactsHandler<'_, '_, '_> { // typeof x === 'object' // => x = {} | null if ty.is_unknown() && self.facts.contains(TypeFacts::TypeofEQObject) { - ty = Type::Union(Union { + ty = Type::new_union( span, - types: vec![ + vec![ Type::Keyword(KeywordType { span, kind: TsKeywordTypeKind::TsObjectKeyword, metadata: Default::default(), tracker: Default::default(), }), - Type::Keyword(KeywordType { - span, - kind: TsKeywordTypeKind::TsNullKeyword, - metadata: Default::default(), - tracker: Default::default(), - }), + Type::null(span, Default::default()), ], - metadata: Default::default(), - tracker: Default::default(), - }) + ) .freezed(); } diff --git a/crates/stc_ts_type_ops/src/union_normalization.rs b/crates/stc_ts_type_ops/src/union_normalization.rs index 083894c56e..fc560680d2 100644 --- a/crates/stc_ts_type_ops/src/union_normalization.rs +++ b/crates/stc_ts_type_ops/src/union_normalization.rs @@ -6,8 +6,8 @@ use rustc_hash::FxHashMap; use stc_ts_ast_rnode::{RBindingIdent, RIdent, RPat}; use stc_ts_generics::type_param::replacer::TypeParamReplacer; use stc_ts_types::{ - CallSignature, FnParam, Function, FunctionMetadata, Key, KeywordType, PropertySignature, Type, TypeElement, TypeLit, TypeLitMetadata, - TypeParamDecl, Union, + CallSignature, FnParam, Function, FunctionMetadata, Key, PropertySignature, Type, TypeElement, TypeLit, TypeLitMetadata, TypeParamDecl, + Union, }; use stc_utils::{cache::Freeze, dev_span, ext::TypeVecExt}; use swc_atoms::JsWord; @@ -357,12 +357,7 @@ impl ObjectUnionNormalizer { }, optional: true, params: Default::default(), - type_ann: Some(Box::new(Type::Keyword(KeywordType { - span: DUMMY_SP, - kind: TsKeywordTypeKind::TsUndefinedKeyword, - metadata: Default::default(), - tracker: Default::default(), - }))), + type_ann: Some(Box::new(Type::undefined(DUMMY_SP, Default::default()))), type_params: Default::default(), metadata: Default::default(), accessor: Default::default(), diff --git a/crates/stc_ts_types/src/lib.rs b/crates/stc_ts_types/src/lib.rs index dd622bf37e..58ab82b75c 100644 --- a/crates/stc_ts_types/src/lib.rs +++ b/crates/stc_ts_types/src/lib.rs @@ -1991,6 +1991,19 @@ impl Type { }) } + pub fn null_and_undefined(span: Span, metadata: KeywordTypeMetadata) -> Vec { + vec![Self::null(span, metadata), Self::undefined(span, metadata)] + } + + pub fn null(span: Span, metadata: KeywordTypeMetadata) -> Self { + Type::Keyword(KeywordType { + span, + kind: TsKeywordTypeKind::TsNullKeyword, + metadata, + tracker: Default::default(), + }) + } + pub fn undefined(span: Span, metadata: KeywordTypeMetadata) -> Self { Type::Keyword(KeywordType { span,