# Scenario When writing code that will run in both Node.js and browser environments (e.g. a React component compatible with SSR), it's sometimes necessary that part of the code only executes in one environment and not the other. A few browser-only examples: - `document.querySelector()` to find an element in the DOM. - `document.title = "new title"` to update the page title. To avoid executing these in Node.js, the following pattern is used: ```ts if (typeof document !== "undefined") { // Safely use `document`. document.title = "new title"; } ``` Now the developer needs to configure TypeScript to include the types for `document`. It basically boils down to them deciding whether or not to include `dom` in the `lib` compiler option. (`dom.d.ts` is distributed with TypeScript and includes:) ```ts declare var document: Document; ``` Unfortunately both options have compromises: - **don't include it** and `document` won't be declared, and TypeScript will warn `Cannot find name 'document'.` - **do include it** and `document` will be declared as a `Document`, but not as `Document | undefined`, so [`strict-type-predicates`](https://palantir.github.io/tslint/rules/strict-type-predicates/) will complain that the check is unnecessary A motivated developer may choose to fork `dom.d.ts` and add `| undefined` to all of the global variable declarations, and satisfy `strict-type-predicates`. However one final piece of safety is still missing: ensuring `typeof document !== "undefined"` is used, rather than `document !== undefined`. At runtime if `document` (or any name) isn't declared, and is used outside `typeof`, a `ReferenceError` is thrown. For this reason it's necessary to always choose `typeof` when performing environment "sniffing". TypeScript currently cannot model "optionally declared variables", where a variable "might" be declared. Without this, TypeScript doesn't have enough information to warn about code that _may_ throw `ReferenceError`. # Proposal Introduce new syntax and type system concepts to allow the "optional declaration" of variables. Emit would be unaffected. **Proposed syntax:** ```ts declare? var window: Window; ``` This is distinct from: ```ts declare var window: Window | undefined; ``` In the first case, it's describing that `window` may not be declared, and referencing `window` may throw a `ReferenceError`. In the second case, `window` is *always* declared, but might have the value `undefined`. **Implementation:** - The grammar would need to be extended to match `?` following `declare`. - The AST would need a representation (e.g. a flag for `DeclareKeyword` or a new `OptionalDeclareKeyword`). - The type system would need to track if a variable is optionally-declared, and support type-narrowing to a standard variable via `typeof` guards. - For this to actually be useful, the type checker should warn if "optionally declared" variables are referenced. - If a variable is "optionally declared", that information should be included in the string representation (e.g. `declare? var document: Document`), e.g. when hovering a name in VS Code. **Shortcomings:** - It would be convenient if a single `typeof window !== "undefined"` guard could safely grant access to other browser globals (e.g. `document`, `performance`, etc). This current proposal would require separate `typeof` checks for each global.