tsz_common/checker_options.rs
1//! Compiler options for type checking.
2//!
3//! This module lives in tsz-common so that both the solver and checker
4//! can reference `CheckerOptions` without creating a circular dependency.
5
6use crate::common::{ModuleKind, ScriptTarget};
7
8/// Compiler options for type checking.
9#[derive(Debug, Clone)]
10pub struct CheckerOptions {
11 pub strict: bool,
12 pub no_implicit_any: bool,
13 pub no_implicit_returns: bool,
14 pub strict_null_checks: bool,
15 pub strict_function_types: bool,
16 pub strict_property_initialization: bool,
17 pub no_implicit_this: bool,
18 pub use_unknown_in_catch_variables: bool,
19 pub isolated_modules: bool,
20 /// When true, indexed access with index signatures adds `| undefined` to the type
21 pub no_unchecked_indexed_access: bool,
22 /// When true, checking bind/call/apply uses strict function signatures
23 pub strict_bind_call_apply: bool,
24 /// When true, optional properties are treated as exactly `T | undefined` not `T | undefined | missing`
25 pub exact_optional_property_types: bool,
26 /// When true, no library files (including lib.d.ts) are included.
27 /// This corresponds to the --noLib compiler flag.
28 /// TS2318 errors are emitted when referencing global types with this option enabled.
29 pub no_lib: bool,
30 /// When true, do not automatically inject built-in type declarations.
31 /// This corresponds to the --noTypesAndSymbols compiler flag.
32 /// Prevents loading default lib.d.ts files which provide types like Array, Object, etc.
33 pub no_types_and_symbols: bool,
34 /// Target ECMAScript version (ES3, ES5, ES2015, ES2016, etc.)
35 /// Controls which built-in types are available (e.g., Promise requires ES2015)
36 /// Defaults to ES3 for maximum compatibility
37 pub target: ScriptTarget,
38 /// Module kind (None, `CommonJS`, ES2015, ES2020, ES2022, `ESNext`, etc.)
39 /// Controls which module system is being targeted (affects import/export syntax validity)
40 pub module: ModuleKind,
41 /// Emit additional JavaScript to ease support for importing `CommonJS` modules.
42 /// When true, synthesizes default exports for `CommonJS` modules.
43 pub es_module_interop: bool,
44 /// Allow 'import x from y' when a module doesn't have a default export.
45 /// Implied by esModuleInterop.
46 pub allow_synthetic_default_imports: bool,
47 /// Controls reporting of unreachable code (TS7027).
48 /// - `None` (default): tsc emits TS7027 as a suggestion, not an error
49 /// - `Some(false)`: tsc emits TS7027 as an error
50 /// - `Some(true)`: tsc does not emit TS7027 at all
51 pub allow_unreachable_code: Option<bool>,
52 /// When true, require bracket notation for index signature property access (TS4111).
53 pub no_property_access_from_index_signature: bool,
54 /// When true, enable Sound Mode for stricter type checking beyond TypeScript's defaults.
55 /// Sound Mode catches common unsoundness issues like:
56 /// - Mutable array covariance (TS9002)
57 /// - Method parameter bivariance (TS9003)
58 /// - `any` escapes (TS9004)
59 /// - Excess properties via sticky freshness (TS9001)
60 ///
61 /// Activated via: `--sound` CLI flag or `// @ts-sound` pragma
62 pub sound_mode: bool,
63 /// When true, enables experimental support for decorators (legacy decorators).
64 /// This is required for the @experimentalDecorators flag.
65 /// When decorators are used, `TypedPropertyDescriptor` must be available.
66 pub experimental_decorators: bool,
67 /// When true, report errors for unused local variables (TS6133).
68 pub no_unused_locals: bool,
69 /// When true, report errors for unused function parameters (TS6133).
70 pub no_unused_parameters: bool,
71 /// When true, parse in strict mode and emit "use strict" for each source file.
72 /// Enables TS1100 for invalid use of 'arguments' in strict mode.
73 pub always_strict: bool,
74 /// When true, allows importing JSON files with `.json` extension.
75 /// When false, importing JSON files emits TS2732 suggesting to enable this flag.
76 pub resolve_json_module: bool,
77 /// When true, enable type checking in JavaScript files.
78 /// This corresponds to the --checkJs compiler flag.
79 /// With checkJs enabled, noImplicitAny and other type errors apply to .js files.
80 pub check_js: bool,
81 /// When true, disable dependency expansion from imports and triple-slash references.
82 pub no_resolve: bool,
83 /// When true, check side-effect imports for module resolution errors (TS2882).
84 pub no_unchecked_side_effect_imports: bool,
85 /// When true, require 'override' modifier on members that override base class members (TS4114).
86 pub no_implicit_override: bool,
87 /// JSX factory function (e.g. `React.createElement`)
88 pub jsx_factory: String,
89 /// JSX fragment factory function (e.g. `React.Fragment`)
90 pub jsx_fragment_factory: String,
91 /// JSX emit mode (preserve, react, react-jsx, react-jsxdev, react-native).
92 /// Only "react" (classic transform) requires the factory to be in scope.
93 pub jsx_mode: JsxMode,
94}
95
96/// JSX emit mode controlling how JSX is transformed.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
98pub enum JsxMode {
99 /// No JSX mode specified (default — treated same as None/no JSX).
100 #[default]
101 None,
102 /// Keep JSX as-is in the output (no factory required in scope).
103 Preserve,
104 /// Classic React transform — requires factory (e.g. `React.createElement`) in scope.
105 React,
106 /// Automatic React transform via `_jsx` — factory NOT required in scope.
107 ReactJsx,
108 /// Development automatic React transform — factory NOT required in scope.
109 ReactJsxDev,
110 /// React Native — preserve JSX (no factory required in scope).
111 ReactNative,
112}
113
114impl Default for CheckerOptions {
115 fn default() -> Self {
116 Self {
117 strict: true,
118 no_implicit_any: true,
119 no_implicit_returns: false,
120 strict_null_checks: true,
121 strict_function_types: true,
122 strict_property_initialization: true,
123 no_implicit_this: true,
124 use_unknown_in_catch_variables: true,
125 isolated_modules: false,
126 no_unchecked_indexed_access: false,
127 strict_bind_call_apply: true,
128 exact_optional_property_types: false,
129 no_lib: false,
130 no_types_and_symbols: false,
131 target: ScriptTarget::default(),
132 module: ModuleKind::default(),
133 es_module_interop: false,
134 allow_synthetic_default_imports: false,
135 allow_unreachable_code: None,
136 no_property_access_from_index_signature: false,
137 sound_mode: false,
138 experimental_decorators: false,
139 no_unused_locals: false,
140 no_unused_parameters: false,
141 // TSC 6.0 defaults: `alwaysStrict !== false` → true when not explicitly set.
142 // This matches TypeScript's behavior where alwaysStrict is true by default.
143 always_strict: true,
144 resolve_json_module: false,
145 check_js: false,
146 no_resolve: false,
147 no_unchecked_side_effect_imports: true,
148 no_implicit_override: false,
149 jsx_factory: "React.createElement".to_string(),
150 jsx_fragment_factory: "React.Fragment".to_string(),
151 jsx_mode: JsxMode::None,
152 }
153 }
154}
155
156impl CheckerOptions {
157 /// Apply TypeScript's `--strict` defaults to individual strict flags.
158 /// In tsc, enabling `strict` turns on the strict family unless explicitly disabled.
159 /// We mirror that behavior by OR-ing the per-flag booleans with `strict`.
160 #[must_use]
161 pub const fn apply_strict_defaults(mut self) -> Self {
162 if self.strict {
163 self.no_implicit_any = true;
164 self.no_implicit_this = true;
165 self.strict_null_checks = true;
166 self.strict_function_types = true;
167 self.strict_bind_call_apply = true;
168 self.strict_property_initialization = true;
169 self.use_unknown_in_catch_variables = true;
170 self.always_strict = true;
171 // exactOptionalPropertyTypes and other opts are not implied by --strict
172 }
173 self
174 }
175}