Parameter attributes
TypeScript's parameter properties are a syntactic sugar that allows declaring and initializing class properties directly within constructor parameters. This approach reduces boilerplate code and makes class definitions more concise.
Basic Syntax of Parameter Properties
By adding an access modifier (public
, private
, or protected
) or the readonly
modifier before a constructor parameter, TypeScript automatically converts that parameter into a class property with the same name. For example:
class Person {
constructor(
public name: string,
private age: number,
readonly id: string
) {}
}
const person = new Person('Alice', 30, '123');
console.log(person.name); // Output: Alice
console.log(person.age); // Error: Property 'age' is private
console.log(person.id); // Output: 123
How Parameter Properties Work
Parameter properties are essentially shorthand for the following traditional syntax:
class Person {
name: string;
private age: number;
readonly id: string;
constructor(name: string, age: number, id: string) {
this.name = name;
this.age = age;
this.id = id;
}
}
The TypeScript compiler converts parameter properties into regular class property declarations and assignment statements in the constructor.
Modifier Combinations for Parameter Properties
Parameter properties support various modifier combinations:
class Employee {
constructor(
public readonly employeeId: string,
protected department: string,
private _salary: number
) {}
}
const emp = new Employee('E1001', 'IT', 50000);
console.log(emp.employeeId); // Output: E1001
emp.employeeId = 'E1002'; // Error: Cannot assign to 'employeeId' because it is a read-only property
Mixing Parameter Properties with Regular Parameters
You can mix parameter properties with regular parameters in a constructor:
class Product {
constructor(
public sku: string,
private cost: number,
description: string
) {
console.log(`Product ${sku} described as: ${description}`);
}
}
const product = new Product('P100', 19.99, 'High-quality widget');
console.log(product.sku); // Output: P100
console.log(product.description); // Error: Property 'description' does not exist
Inheritance Behavior of Parameter Properties
When a subclass inherits from a parent class, the access modifiers of parameter properties remain effective:
class Animal {
constructor(protected name: string) {}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name);
}
bark() {
console.log(`${this.name} the ${this.breed} says woof!`);
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.bark(); // Output: Buddy the Golden Retriever says woof!
console.log(dog.name); // Error: Property 'name' is protected
Type Inference for Parameter Properties
TypeScript performs type checking based on the type annotations of parameter properties:
class Point {
constructor(
public x: number,
public y: number,
public z?: number
) {}
}
const point2D = new Point(1, 2);
const point3D = new Point(3, 4, 5);
const invalidPoint = new Point('a', 'b'); // Error: Type 'string' is not assignable to type 'number'
Parameter Properties and Interface Implementation
Parameter properties can be used to implement properties required by interfaces:
interface Identifiable {
id: string;
}
class User implements Identifiable {
constructor(public id: string, public username: string) {}
}
const user = new User('u1', 'alice');
console.log(user.id); // Output: u1
Compiled Output of Parameter Properties
Examining the compiled JavaScript code helps understand the essence of parameter properties:
// TypeScript source code
class Car {
constructor(public model: string, private vin: string) {}
}
// Compiled JavaScript
"use strict";
class Car {
constructor(model, vin) {
this.model = model;
this.vin = vin;
}
}
Use Cases for Parameter Properties
Parameter properties are particularly suitable for:
-
DTOs (Data Transfer Objects): Quickly define data container classes
class UserDTO { constructor( public id: string, public name: string, public email: string ) {} }
-
Entity Classes: Concisely define domain models
class Order { constructor( public orderId: string, public items: OrderItem[], private _total: number ) {} }
-
Configuration Objects: Initialize configuration options
class AppConfig { constructor( public apiUrl: string, public timeout: number, public debug: boolean ) {} }
Considerations for Parameter Properties
-
Initialization Order: Parameter properties are initialized before the constructor body executes
class Counter { count = 0; constructor(public initialValue: number) { this.count = initialValue; // Overrides the initial value of the parameter property } }
-
Decorator Limitations: Parameter properties cannot directly use parameter decorators
class Example { constructor(@SomeDecorator public param: string) {} // Error }
-
Documentation Generation: Some documentation tools may not correctly recognize parameter properties
Parameter Properties in React Components
In React class components, parameter properties can simplify props type definitions:
interface Props {
title: string;
count: number;
}
class MyComponent extends React.Component<Props> {
constructor(props: Props) {
super(props);
}
// Simplified using parameter properties
constructor(public props: Props) {
super(props);
}
}
Alternatives to Parameter Properties
If parameter properties are not used, similar effects can be achieved through other methods:
-
Traditional Property Declaration:
class Traditional { prop: string; constructor(prop: string) { this.prop = prop; } }
-
Object.assign:
class AssignExample { constructor(options: any) { Object.assign(this, options); } }
-
Destructuring Assignment:
class DestructureExample { constructor({ a, b }: { a: string; b: number }) { this.a = a; this.b = b; } a: string; b: number; }
Performance Considerations for Parameter Properties
Parameter properties do not introduce additional runtime overhead, as they are compiled into regular property assignments. The following example shows the comparison before and after compilation:
// Before compilation
class CompileExample {
constructor(public a: string, private b: number) {}
}
// After compilation
class CompileExample {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
Parameter Properties and Mapped Types
Combining with mapped types allows creating dynamic properties:
type Partial<T> = {
[P in keyof T]?: T[P];
};
class Configurable {
constructor(config: Partial<Configurable>) {
Object.assign(this, config);
}
id?: string;
name?: string;
}
Parameter Properties and Generics
Parameter properties can be used with generics:
class GenericExample<T> {
constructor(public value: T, private _default: T) {}
reset() {
this.value = this._default;
}
}
const example = new GenericExample<string>('hello', 'default');
console.log(example.value); // Output: hello
example.reset();
console.log(example.value); // Output: default
Testing Considerations for Parameter Properties
When using parameter properties, testing should account for:
class Testable {
constructor(public dependency: SomeService) {}
method() {
return this.dependency.doSomething();
}
}
// In tests
const mockService = { doSomething: jest.fn() };
const instance = new Testable(mockService);
instance.method();
expect(mockService.doSomething).toHaveBeenCalled();
Parameter Properties and Dependency Injection
Parameter properties can simplify dependency injection code:
class InjectExample {
constructor(
@inject('ServiceA') public serviceA: ServiceA,
@inject('ServiceB') private serviceB: ServiceB
) {}
}
Code Style Recommendations for Parameter Properties
- Consistency: Uniformly use or avoid parameter properties across the project
- Readability: Use parameter properties for simple classes and explicit declarations for complex logic
- Documentation: Add JSDoc comments for parameter properties
class Documented { /** * Creates a documented example * @param value The primary value * @param count The counter */ constructor(public value: string, private count: number) {} }
Tool Support for Parameter Properties
Most TypeScript toolchains handle parameter properties correctly:
- TypeScript Playground: Allows real-time viewing of compiled results
- VS Code: Provides IntelliSense and type checking
- ESLint: Includes rules for checking parameter property usage
Historical Evolution of Parameter Properties
Parameter properties were introduced in TypeScript 1.5 and have gradually become a common practice:
- TypeScript 1.5 (July 2015): Initial introduction
- TypeScript 2.0: Improved type checking
- TypeScript 3.0: Better integration with generics
Comparison with Other Languages
Comparing with similar features in other languages:
- C#: Primary Constructors proposal
- Kotlin: Direct property declaration in constructors
class Person(val name: String, var age: Int)
- Swift: Requires explicit storage property declarations
Advanced Patterns with Parameter Properties
Combining with other TypeScript features enables more complex patterns:
function createEntity<T>() {
return class Entity {
constructor(public props: T) {}
};
}
const UserEntity = createEntity<{ name: string; age: number }>();
const user = new UserEntity({ name: 'Alice', age: 30 });
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:装饰器基础与应用