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.”
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();
- TypeScript reads
IXDraftand sees thatais required. - It sees
b?andc?, so those properties may be absent initially. { a: "abc" }satisfies the interface because it includes the only required property.x.b = 123adds or updatesb;unknowncan hold any assigned value.x.c = new AnotherType()adds or updatescwith the correct type.- Until
cis assigned, readingx.cproduces a value that may beundefined. 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
createConfigfunction 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.
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
undefinedusually means a property was omitted or has not been assigned.nullis 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
| Approach | Can properties be omitted initially? | Provides runtime defaults? | Best use |
|---|---|---|---|
| Required interface properties | No | No | Values that must always be complete |
Optional properties (prop?) | Yes | No | Drafts and partial input |
Nullable property (prop: T | null) | No; property must exist | No | A field exists but explicitly has no value |
| Factory function | Depends on its input type | Yes | Consistent, complete objects with defaults |
| Class fields / constructor | Depends on constructor parameters | Yes | Objects with initialization behavior |
Partial<T> | Yes, for every property | No | Temporary 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.
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 | nullonly whennullis a meaningful valid value. - Prefer
unknownoveranyfor values whose type is not yet known. - Avoid
as SomeTypeto 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.
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
- Define an
AppSettingsinterface withtheme,pageSize, andshowTipsproperties. - Use
Partial<AppSettings>as the function input type. - Provide defaults for all three settings.
- Allow a caller to override only
theme. - Log the final complete settings object.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.