Question

TypeScript Interface Default Values and Optional Properties

Question

Given this TypeScript interface:

interface IX {
  a: string;
  b: unknown;
  c: AnotherType;
}

I create an object of type IX and initially use placeholder values because some properties are assigned later:

let x: IX = {
  a: "abc",
  b: null,
  c: null
};

x.a = "xyz";
x.b = 123;
x.c = new AnotherType();

Can an interface specify that properties not supplied in an object literal should default to null, so that this is allowed without a compiler error?

let x: IX = {
  a: "abc"
};

Currently, TypeScript reports that required properties such as b and c are missing.

Short Answer

You will learn the difference between a TypeScript interface and a runtime object, why interfaces cannot provide default values, and how to choose between optional properties, factory functions, and staged initialization safely.

Concept

An interface is a compile-time type contract. It describes which properties a value must have and what their types are. It does not create an object, run code, or assign values at runtime.

In this interface, all three properties are required:

interface IX {
  a: string;
  b: unknown;
  c: AnotherType;
}

Therefore, an IX value must contain a, b, and c at the point where TypeScript checks it. This is why the following is rejected:

const x: IX = { a: "abc" };
// Error: properties b and c are missing

Interfaces cannot declare defaults such as b = null. Defaults are runtime behavior, so they belong in code that creates values, such as a function or a class constructor.

If a property may genuinely be absent while an object is being initialized, mark it optional with ?:

interface IXDraft {
  a: string;
  b?: unknown;
  c?: AnotherType;
}

Now { a: "abc" } is valid as an IXDraft. However, code reading b or c must handle the possibility that the value is undefined.

This distinction prevents a common bug: treating a partially initialized object as though it were ready to use.

Mental Model

Think of an interface as a checklist for an inspection, not a machine that builds an object.

  • A required property is an item that must already be present on the checklist.
  • An optional property is an item that may be missing.
  • A factory function or constructor is the machine that can actually fill in default values.

Writing interface IX { b: unknown } tells TypeScript, “Every finished IX has b.” It does not tell JavaScript, “Create b with null when it is missing.”

Take Quiz

Syntax and Examples

A property without ? is required:

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "Ada",
  age: 36
};

Use ? when a property can be omitted:

interface UserDraft {
  name: string;
  age?: number;
}

const draft: UserDraft = {
  name: "Ada"
};

An optional property is potentially undefined when read:

function describeUser(user: UserDraft): string {
  if (user.age === undefined) {
    return `${user.name}'s age is unknown`;
  }

  return `${user.name} is ${user.age}`;
}

If you want actual runtime defaults, use a function:

class AnotherType {}

interface IX {
  a: string;
  b: unknown;
  c: AnotherType | null;
}

function createIX(a: string): IX {
  return {
    a,
    b: null,
    c: null
  };
}

const x = createIX("abc");

Here the interface still describes the completed shape, while createIX performs the runtime initialization.

Step by Step Execution

Consider a draft object that becomes complete later:

class AnotherType {}

interface IXDraft {
  a: string;
  b?: unknown;
  c?: AnotherType;
}

const x: IXDraft = { a: "abc" };
x.b = 123;
x.c = new AnotherType();
  1. TypeScript reads IXDraft and sees that a is required.
  2. It sees b? and c?, so those properties may be absent initially.
  3. { a: "abc" } satisfies the interface because it includes the only required property.
  4. x.b = 123 adds or updates b; unknown can hold any assigned value.
  5. x.c = new AnotherType() adds or updates c with the correct type.
  6. Until c is assigned, reading x.c produces a value that may be undefined. Code must check it before using it:
if (x.c) {
  // x.c is AnotherType in this block
}

If the rest of the program requires all fields, convert the draft into a complete object only after validation.

Real World Use Cases

  • Form drafts: A user may enter a name before entering an address or payment details. A draft type can make unfinished fields optional.
  • API request construction: Build request options in stages, then validate required values before sending the request.
  • Configuration: Apply defaults in a createConfig function so application code receives a complete configuration object.
  • Data loading: Represent data that has not arrived yet with undefined, null, or a separate loading state instead of pretending it is already available.
  • Object mapping: Convert incomplete data from a form or API into a complete domain object once all required fields are known.
Take Quiz

Real Codebase Usage

In production TypeScript, developers usually avoid a mutable object that is typed as complete before it is complete. Common patterns include:

Use a draft type and validate it

interface IX {
  a: string;
  b: number;
  c: AnotherType;
}

type IXDraft = Partial<IX> & Pick<IX, "a">;

function finalize(draft: IXDraft): IX {
  if (draft.b === undefined) {
    throw new Error("b is required");
  }
  if (draft.c === undefined) {
    throw new Error("c is required");
  }

  return { a: draft.a, b: draft.b, c: draft.c };
}

Partial<IX> makes every property optional. Pick<IX, "a"> makes a required again.

Apply defaults with a factory function

interface Settings {
  retries: number;
  timeoutMs: number;
}

function createSettings(overrides: Partial<Settings> = {}): Settings {
  return {
    retries: 3,
    timeoutMs: 5_000,
    ...overrides
  };
}

This is a strong choice when every consumer should receive a complete object.

Use a class when initialization is object behavior

class ConnectionOptions {
  host = "localhost";
  port = 5432;
}

Class field initializers run at runtime, unlike interface declarations.

Common Mistakes

Expecting an interface to assign values

This is not valid TypeScript syntax and would not create runtime defaults even if it were:

// Incorrect
interface IX {
  b: unknown = null;
}

Use a factory function, constructor, or class field initializer for defaults.

Making fields optional without handling undefined

interface IXDraft {
  c?: AnotherType;
}

function useValue(x: IXDraft) {
  x.c.doSomething(); // Error: x.c may be undefined
}

Check first:

function useValue(x: IXDraft) {
  if (x.c) {
    x.c.doSomething();
  }
}

Confusing null and undefined

  • undefined usually means a property was omitted or has not been assigned.
  • null is an explicit value meaning “intentionally no value.”

With strictNullChecks enabled, null is not assignable to AnotherType:

interface IX {
  c: AnotherType;
}

const x: IX = { a: "abc", b: null, c: null }; // Error with strictNullChecks

If null is a valid state, state it explicitly: c: AnotherType | null.

Using any for an unknown value

any disables useful checking. Prefer unknown when the type is not known yet, then narrow it before use.

let value: unknown = 123;

if (typeof value === "number") {
  console.log(value.toFixed(2));
}

Using a type assertion to hide missing fields

const x = { a: "abc" } as IX; // Unsafe

This silences the compiler but does not add b or c to the object. A later read can fail at runtime.

Comparisons

ApproachCan properties be omitted initially?Provides runtime defaults?Best use
Required interface propertiesNoNoValues that must always be complete
Optional properties (prop?)YesNoDrafts and partial input
Nullable property (prop: T | null)No; property must existNoA field exists but explicitly has no value
Factory functionDepends on its input typeYesConsistent, complete objects with defaults
Class fields / constructorDepends on constructor parametersYesObjects with initialization behavior
Partial<T>Yes, for every propertyNoTemporary patches, drafts, and builder input

prop?: T and prop: T | undefined are related but not identical. Optional properties may be omitted entirely; a required prop: T | undefined property must still be present in an object literal.

Take Quiz

Cheat Sheet

// Required: must be supplied
interface A {
  value: string;
}

// Optional: may be omitted; reading may produce undefined
interface B {
  value?: string;
}

// Required property whose value may explicitly be null
interface C {
  value: string | null;
}

// Runtime defaults: use a function
function createOptions(input: Partial<Options> = {}): Options {
  return { enabled: true, ...input };
}
  • Interfaces describe shapes; they do not execute code.
  • Required properties must exist when assigning an object to that interface.
  • ? means a property may be missing.
  • Check optional properties before using them.
  • Use T | null only when null is a meaningful valid value.
  • Prefer unknown over any for values whose type is not yet known.
  • Avoid as SomeType to bypass missing-property errors.

FAQ

Can TypeScript interfaces have default values?

No. Interfaces are removed when TypeScript compiles to JavaScript, so they cannot create or initialize properties. Use a factory function, constructor, or class field initializer.

How do I make an interface property optional?

Add ? after its name:

interface IX {
  b?: unknown;
}

Does an optional property default to null?

No. If it is not supplied, its value is normally undefined when read. Use null only when you explicitly assign it and include null in the property's type if strict null checking is enabled.

Should I use null or undefined for uninitialized properties?

Use undefined or an optional property when a value has not been supplied. Use null when “no value” is an intentional state that your program needs to represent.

Why does { a: "abc" } as IX compile?

A type assertion tells TypeScript to trust you. It does not add missing properties at runtime, so it can create bugs. Prefer proper initialization or a draft type.

What is Partial<IX>?

Partial<IX> is a utility type that makes all properties in IX optional. It is useful for updates and drafts, but it should not be used where a complete IX is required.

Can I assign null to a property typed as AnotherType?

Not when strictNullChecks is enabled. Write AnotherType | null if null is intended to be valid.

Take Quiz

Mini Project

Description

Create a settings factory for a small application. Callers may provide only the settings they want to override, while the application always receives a complete settings object with safe defaults.

Goal

Build a createAppSettings function that merges optional overrides with required runtime defaults.

Requirements

  1. Define an AppSettings interface with theme, pageSize, and showTips properties.
  2. Use Partial<AppSettings> as the function input type.
  3. Provide defaults for all three settings.
  4. Allow a caller to override only theme.
  5. Log the final complete settings object.
Take Quiz

Keep learning

Related questions

View more