Type checking in strict mode
TypeScript's strict type-checking mode is a collection of compiler options that significantly enhance code type safety by enabling these options. Under strict mode, the type system catches more potential errors, reduces the likelihood of runtime exceptions, and enforces stricter type definitions from developers.
Core Options of Strict Mode
The strict
flag in tsconfig.json
is essentially a shortcut for the following seven independent options:
{
"compilerOptions": {
"strict": true,
/* Equivalent to enabling:
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true
*/
}
}
In-Depth Analysis of noImplicitAny
When the compiler cannot infer a variable's type, it defaults to any
. Enabling noImplicitAny
triggers an error in such cases:
// Error example
function logValue(value) { // Error: Parameter implicitly has 'any' type
console.log(value.toFixed(2));
}
// Correct approach
function logValue(value: number) {
console.log(value.toFixed(2));
}
This option is particularly useful for large-scale project migrations. Consider a legacy JavaScript file:
// legacy.js
export function calculateTotal(items) { // Marked as an error during migration
return items.reduce((sum, item) => sum + item.price, 0);
}
The step-by-step fix would be:
interface Item {
price: number;
}
export function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Practical Applications of strictNullChecks
This option prevents null
and undefined
from being assigned to other types:
let userName: string = null; // Error
// Correct approach
let userName: string | null = null;
// Real-world example: API response handling
interface ApiResponse<T> {
data: T | null;
error: string | null;
}
function processResponse(response: ApiResponse<string>) {
if (response.data !== null) {
console.log(response.data.toUpperCase()); // Safe access
}
}
It is especially useful when working with DOM elements:
// Without strictNullChecks
const element = document.getElementById('not-exist');
element.addEventListener('click', () => {}); // Runtime error
// With strictNullChecks enabled
const element = document.getElementById('not-exist');
if (element) { // Null check required
element.addEventListener('click', () => {});
}
Contravariance Checks with strictFunctionTypes
This option enforces stricter contravariance checks for function parameter types:
type Handler = (request: { id: string }) => void;
const handler1: Handler = (req: {}) => {}; // Error
const handler2: Handler = (req: { id: string, extra: number }) => {}; // Correct
// Real-world scenario
class EventEmitter<T> {
private handlers: ((payload: T) => void)[] = [];
register(handler: (payload: T) => void) {
this.handlers.push(handler);
}
}
const stringEmitter = new EventEmitter<string>();
stringEmitter.register((data: string | number) => {}); // Error
Controlling Class Properties with strictPropertyInitialization
Forces class properties to be initialized in the constructor:
class User {
name: string; // Error: Not initialized
constructor() {
// Forgot to initialize 'name'
}
}
// Solution 1: Explicit initialization
class User {
name: string = '';
}
// Solution 2: Using definite assignment assertion
class User {
name!: string; // Tells the compiler we'll assign it later
}
Particularly useful in React components:
class Profile extends React.Component {
state: UserState; // Error
constructor(props: Props) {
super(props);
// Must initialize state here
this.state = { /*...*/ };
}
}
Proper Usage of strictBindCallApply
Ensures call
, apply
, and bind
methods use the correct parameter types:
function greet(name: string, age: number) {
return `Hello ${name}, you are ${age} years old`;
}
const bound1 = greet.bind(null, "Alice"); // Error: Missing 'age' parameter
const bound2 = greet.bind(null, "Alice", 30); // Correct
// Real-world application: Event handler binding
class ButtonComponent {
handleClick(id: number, event: MouseEvent) {
console.log(`Button ${id} clicked`);
}
setup() {
const button = document.querySelector('button');
// Error: Incomplete binding parameters
button?.addEventListener('click', this.handleClick.bind(this, 1));
// Correct: Using a wrapper function
button?.addEventListener('click', (e) => this.handleClick(1, e));
}
}
Context Control with noImplicitThis
Prevents implicit any
typing for this
:
class Rectangle {
width = 10;
height = 20;
getArea() {
return function() {
return this.width * this.height; // Error: 'this' implicitly has type 'any'
}
}
// Correct approach 1: Arrow function
getAreaCorrect() {
return () => {
return this.width * this.height;
}
}
// Correct approach 2: Explicit typing
getAreaCorrect2(this: Rectangle) {
return this.width * this.height;
}
}
Especially important in Vue's Options API:
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++; // Requires noImplicitThis
}
}
}
Parsing Mode with alwaysStrict
Forces strict mode parsing for all code, equivalent to adding "use strict"
at the top of every file:
// The following code works in non-strict mode but errors with alwaysStrict
function leaky() {
leakedVar = 10; // Error: Undeclared variable
}
// Changes in strict mode
delete Object.prototype; // Error: Deleting non-configurable properties is prohibited in strict mode
Gradual Adoption Strategy for Strict Mode
For existing projects, it's recommended to enable strict options incrementally:
- First enable
noImplicitAny
andstrictNullChecks
- Then enable
strictFunctionTypes
andstrictBindCallApply
- Finally enable
strictPropertyInitialization
andnoImplicitThis
Example configuration:
{
"compilerOptions": {
"strict": false,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": false,
"strictPropertyInitialization": false
}
}
Advanced Type Techniques in Strict Mode
Leverage strict mode for more precise type modeling:
// Using 'never' to represent impossible states
type Result<T, E> =
| { success: true; value: T }
| { success: false; error: E }
| never; // Ensures all branches are handled
function processResult(result: Result<string, Error>) {
if (result.success) {
console.log(result.value);
} else {
console.error(result.error);
}
// If a new branch is added but not handled, type checking will error
}
Strict Mode and Third-Party Library Interactions
Handling libraries without type definitions:
// Using type assertions
const unsafeLib = require('untyped-lib') as {
doSomething: (input: string) => number;
};
// Or declaring module types
declare module 'untyped-lib' {
export function doSomething(input: string): number;
}
For potentially null callbacks:
interface LibraryOptions {
onSuccess?: (data: string) => void;
onError?: (err: Error) => void;
}
function useLibrary(options: LibraryOptions) {
// In strict mode, must check if callbacks exist
options.onSuccess?.("data");
}
Performance Considerations in Strict Mode
While strict checks increase compilation time, optimize with:
- Using relaxed configurations for test files
- Disabling strict mode for utility scripts
- Using project references to isolate strict checks
Example configuration:
// tsconfig.build.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"strict": true
}
}
// tsconfig.tools.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"strict": false
}
}
Strict Mode and Test Code
Test code can have relaxed restrictions:
// Allowing 'any' in test configurations
describe('API Client', () => {
let mockResponse: any; // 'any' is allowed in tests
beforeEach(() => {
mockResponse = { data: {}, status: 200 };
});
});
// Or using type assertions
test('handles error', () => {
const error = new Error() as any;
error.code = 'CUSTOM_ERROR';
// ...Test logic
});
Advanced Type Guards in Strict Mode
Using user-defined type guards for complex types:
interface Cat {
meow(): void;
climb(): void;
}
interface Dog {
bark(): void;
run(): void;
}
function isCat(animal: Cat | Dog): animal is Cat {
return 'meow' in animal;
}
function handleAnimal(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // Type narrowed to 'Cat'
} else {
animal.bark(); // Type narrowed to 'Dog'
}
}
Strict Mode and Generic Constraints
Generic constraints become more precise in strict mode:
function mergeObjects<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
// Error example
mergeObjects(undefined, {}); // Violates 'object' constraint
// Proper handling of optional parameters
function safeMerge<T extends object, U extends object>(
a?: T,
b?: U
): Partial<T & U> {
return { ...a, ...b };
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:TypeScript核心知识点
下一篇:TypeScript的生态系统