Skip to main content

Avoid Redundant Optional Property Declarations in TypeScript

High
maintainabilityreliability

What is it?

This practice focuses on avoiding the redundancy of declaring optional properties using both the optional property syntax ('?') and an explicit union with undefined in TypeScript. Combining these two approaches is unnecessary and can cause confusion about the intended usage of the property.

Why apply it?

When you mark a property as optional using the '?' syntax, it already implies that the property might be omitted or be undefined. Adding | undefined to the type declaration is redundant and can mislead developers, potentially complicating type inference and code readability.

How to Fix it?

Choose one convention for declaring optional properties. If you intend for a property to be optional, simply use the '?' syntax without | undefined. Only combine them if you specifically require the behavior provided by the TypeScript compiler option exactOptionalPropertyTypes—but for most cases, the simpler syntax is preferable.

Examples

Example 1:

Positive

The following example correctly uses the optional property syntax without an extra union with undefined. The property address is declared as optional, making the code clear and concise.

interface Person {
name: string;
address?: string; // Compliant: using '?' without '| undefined'
}

function createPerson(name: string, address?: string): Person {
return { name, address };
}

const person1: Person = createPerson("Alice");
const person2: Person = createPerson("Bob", "123 Main St");

console.log("Person 1:", person1);
console.log("Person 2:", person2);

Negative

This example shows a redundancy issue where the optional property address is declared with both '?' and the union type | undefined. This practice is unnecessary and should be avoided unless you have a specific reason tied to compiler settings.

interface Person {
name: string;
address?: string | undefined; // Noncompliant: redundant use of '?' and '| undefined'
}

function createPerson(name: string, address?: string | undefined): Person {
return { name, address };
}

const person1: Person = createPerson("Alice", undefined);
const person2: Person = createPerson("Bob", "123 Main St");

console.log("Person 1:", person1);
console.log("Person 2:", person2);

Example 2:

Positive

In this example, the optional properties in the configuration interface are declared solely with the '?' syntax. This makes the function signature clean and allows the TypeScript compiler to correctly interpret missing properties as undefined.

interface Config {
timeout?: number;
endpoint?: string;
}

function setup(config: Config): void {
const timeout = config.timeout ?? 3000;
const endpoint = config.endpoint ?? "http://localhost";
console.log(`Setting up with a timeout of ${timeout}ms and endpoint ${endpoint}`);
}

setup({ timeout: 5000 }); // Compliant: using optional properties correctly
setup({}); // Compliant: properties omitted as needed

Negative

Here, the configuration interface mistakenly declares its optional properties using both the '?' syntax and an explicit union with undefined, which creates redundant type definitions.

interface Config {
timeout?: number | undefined; // Noncompliant: redundant declaration with '?' and '| undefined'
endpoint?: string | undefined; // Noncompliant: redundant declaration with '?' and '| undefined'
}

function setup(config: Config): void {
const timeout = (typeof config.timeout === "number") ? config.timeout : 3000;
const endpoint = config.endpoint || "http://localhost";
console.log(`Setting up with a timeout of ${timeout}ms and endpoint ${endpoint}`);
}

setup({ timeout: 5000, endpoint: undefined }); // Noncompliant usage due to redundant type declarations