如何处理可选对象参数:确保属性存在性、合法性及默认值设置
Great questions—let’s break this down step by step since handling optional options parameters while enforcing type safety is super common in TypeScript (I’m assuming you’re using TS given the interface mention, but the logic applies to JS too with minor tweaks).
The core idea is to normalize the input first (to guarantee properties exist) then validate their legitimacy to catch invalid values early.
JavaScript Implementation
function createUser(options = {}) { // Destructure with defaults to ensure properties exist, even if omitted const { isNew = false, name } = options; // Validate validity: throw clear type errors for invalid values if (typeof isNew !== 'boolean') { throw new TypeError('isNew must be a boolean value'); } if (!name || typeof name !== 'string') { throw new TypeError('name is required and must be a string'); } return { name, isNew }; }
TypeScript Implementation
Use an interface to define input shape, then add runtime validation (since TypeScript doesn’t enforce types at runtime):
interface UserOptions { name: string; // Required input property isNew?: boolean; // Optional input property } function createUser(options: UserOptions) { const { isNew = false } = options; // Guard against invalid runtime inputs (e.g., someone passing a string via JS) if (typeof isNew !== 'boolean') { throw new TypeError('isNew must be a boolean'); } return { ...options, isNew }; }
You don’t need to define a separate non-optional interface for your final User instance—there are cleaner, more maintainable approaches to guarantee isNew is always a boolean.
Best Approach (Type Safety + Runtime Guard)
Combine TypeScript’s type system to describe input/output shapes with runtime validation to enforce correctness:
// Input interface: allows optional isNew interface UserOptions { name: string; isNew?: boolean; } // Output interface: enforces required isNew interface User { name: string; isNew: boolean; } function createUser(options: UserOptions): User { const normalizedIsNew = options.isNew ?? false; // Fallback to default if undefined // Critical runtime check: ensure isNew is never a non-boolean value if (typeof normalizedIsNew !== 'boolean') { throw new TypeError('isNew must be a boolean (received: ${typeof normalizedIsNew})'); } // Return an object that strictly matches the User interface return { ...options, isNew: normalizedIsNew }; } // Usage examples: const validUser1 = createUser({ name: "Alice" }); // isNew = false (valid) const validUser2 = createUser({ name: "Bob", isNew: true }); // valid const invalidUser = createUser({ name: "Charlie", isNew: "yes" }); // Throws TypeError
Alternative: Use TypeScript’s Required Utility
If you want to avoid defining a separate User interface, use Required<UserOptions> to mark all properties as required—but note this will enforce all input properties to exist in the output (adjust if only isNew needs to be required):
function createUser(options: UserOptions): Required<UserOptions> { const normalizedOptions = { isNew: false, ...options }; // Same runtime validation as above if (typeof normalizedOptions.isNew !== 'boolean') { throw new TypeError('isNew must be a boolean'); } return normalizedOptions; }
Key Takeaway
Normalize your input with defaults to guarantee property existence, then add runtime checks to enforce validity. You don’t need duplicate interfaces—just leverage TypeScript to distinguish input vs output shapes, and guard against invalid runtime values that TypeScript can’t catch on its own.
内容的提问来源于stack exchange,提问作者tza162

