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 `// @tsz-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 /// Whether the `module` option was explicitly set by the user (via tsconfig or CLI).
95 /// When false, the module kind was computed from defaults (e.g. based on target).
96 /// tsc only emits TS1202 (import assignment in ESM) when module is explicitly set.
97 pub module_explicitly_set: bool,
98 /// When true, suppress TS2353 (excess property) errors.
99 /// This is a removed option (TS5102) but tsc still honors its suppression behavior.
100 pub suppress_excess_property_errors: bool,
101 /// When true, suppress TS7053 (implicit any index) errors.
102 /// This is a removed option (TS5102) but tsc still honors its suppression behavior.
103 pub suppress_implicit_any_index_errors: bool,
104}
105
106/// JSX emit mode controlling how JSX is transformed.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum JsxMode {
109 /// No JSX mode specified (default — treated same as None/no JSX).
110 #[default]
111 None,
112 /// Keep JSX as-is in the output (no factory required in scope).
113 Preserve,
114 /// Classic React transform — requires factory (e.g. `React.createElement`) in scope.
115 React,
116 /// Automatic React transform via `_jsx` — factory NOT required in scope.
117 ReactJsx,
118 /// Development automatic React transform — factory NOT required in scope.
119 ReactJsxDev,
120 /// React Native — preserve JSX (no factory required in scope).
121 ReactNative,
122}
123
124impl Default for CheckerOptions {
125 fn default() -> Self {
126 Self {
127 strict: true,
128 no_implicit_any: true,
129 no_implicit_returns: false,
130 strict_null_checks: true,
131 strict_function_types: true,
132 strict_property_initialization: true,
133 no_implicit_this: true,
134 use_unknown_in_catch_variables: true,
135 isolated_modules: false,
136 no_unchecked_indexed_access: false,
137 strict_bind_call_apply: true,
138 exact_optional_property_types: false,
139 no_lib: false,
140 no_types_and_symbols: false,
141 target: ScriptTarget::default(),
142 module: ModuleKind::default(),
143 es_module_interop: false,
144 allow_synthetic_default_imports: false,
145 allow_unreachable_code: None,
146 no_property_access_from_index_signature: false,
147 sound_mode: false,
148 experimental_decorators: false,
149 no_unused_locals: false,
150 no_unused_parameters: false,
151 // TSC 6.0 defaults: `alwaysStrict !== false` → true when not explicitly set.
152 // This matches TypeScript's behavior where alwaysStrict is true by default.
153 always_strict: true,
154 resolve_json_module: false,
155 check_js: false,
156 no_resolve: false,
157 no_unchecked_side_effect_imports: false,
158 no_implicit_override: false,
159 jsx_factory: "React.createElement".to_string(),
160 jsx_fragment_factory: "React.Fragment".to_string(),
161 jsx_mode: JsxMode::None,
162 module_explicitly_set: false,
163 suppress_excess_property_errors: false,
164 suppress_implicit_any_index_errors: false,
165 }
166 }
167}
168
169impl CheckerOptions {
170 /// Apply TypeScript's `--strict` defaults to individual strict flags.
171 /// In tsc, enabling `strict` turns on the strict family unless explicitly disabled.
172 /// We mirror that behavior by OR-ing the per-flag booleans with `strict`.
173 #[must_use]
174 pub const fn apply_strict_defaults(mut self) -> Self {
175 if self.strict {
176 self.no_implicit_any = true;
177 self.no_implicit_this = true;
178 self.strict_null_checks = true;
179 self.strict_function_types = true;
180 self.strict_bind_call_apply = true;
181 self.strict_property_initialization = true;
182 self.use_unknown_in_catch_variables = true;
183 self.always_strict = true;
184 // exactOptionalPropertyTypes and other opts are not implied by --strict
185 }
186 self
187 }
188}