Ensure existance of optional property in typescript interface

Typescript does understand typeguards like you use, the problem is that they only affect the type of the field not the whole object . So for example under strict null checks we would get the following :

function stringNotUndefined(s: string) {}


function otherFunction(data: Test) {
    stringNotUndefined(data.prop2) // error
    if (data.prop2) {
        stringNotUndefined(data.prop2) //ok
        someFunction(data); // still error
    }
}

We can create a custom type guard that will mark the checked fields as non undefined :

interface Test {
    prop1: string;
    prop2?: string;
}
function someFunction(data: { prop1: string, prop2: string }) {
    console.log(data.prop1 + ": " + data.prop2);
}

type MakeRequired<T,K extends keyof T> = Pick<T, Exclude<keyof T, K>> & {[P in K]-?:Exclude<T[P],undefined> }
function checkFields<T, K extends keyof T>(o: T | MakeRequired<T,K>, ...fields: K[]) : o is MakeRequired<T,K>{
    return fields.every(f => !!o[f]);
}

function otherFunction(data: Test) {
    if (checkFields(data, 'prop2')) {
        someFunction(data); // prop2 is now marked as mandatory, works 
    }
}

Edit

The above version may have a bit too much overhead for such a simple check. We can create a much simpler version for just one field (and use && for more fields). This version has a lot less overhead and might even be inlined if on a hot path.

interface Test {
    prop1?: string;
    prop2?: string;
}
function someFunction(data: { prop1: string, prop2: string }) {
    console.log(data.prop1 + ": " + data.prop2);
}

type MakeRequired<T,K extends keyof T> = Pick<T, Exclude<keyof T, K>> & {[P in K]-?:Exclude<T[P],undefined> }
function checkField<T, K extends keyof T>(o: T | MakeRequired<T,K>,field: K) : o is MakeRequired<T,K>{
    return !!o[field]
}

function otherFunction(data: Test) {
    if (checkField(data, 'prop2') && checkField(data, 'prop1')) {
        someFunction(data); // prop2 is now marked as mandatory, works 
    }
}

If you want a specific type guard instead of the general solution of Titian Cernicova-Dragomir, you can write a simple function.

interface TestWithProp2 {
    prop1: string;
    prop2: string; // required
}

// Special return type
function isTestWithProp2(x: Test): x is TestWithProp2 {
    return x.prop2 !== undefined;
}

// Use
if (isTestWithProp2(data)) {
    someFunction(data);
}

Source: Typescript: User defined type guards

Tags:

Typescript