Prevent compilation unless all fields exist

75 views Asked by At

I have this typescript code:

interface Foo {
  foo1: string,
  foo2: number,
  foo3: boolean
}


const v = <Foo>{foo1: '4'};

this compiles - how can I prevent it from compiling unless all 3 fields are present in the object? Right now only one of the fields is present.

Here is my use case / rationale: https://gist.github.com/ORESoftware/8d02fb30c53c19f6b38bddbc96da2475

But if you do it like so:

const v : Foo = {foo1: '4'};

then it won't compile. And if you do it like so:

  const v = <Foo>{x: '4'};

that won't compile.

1

There are 1 answers

0
zeh On

It will compile because you're doing an assertion. You're basically saying "Trust me, TypeScript compiler, this is of the right type". That's circumventing the check altogether.

If your object satisfies the type, you don't need to do the assertion. Interfaces in TypeScript work by actually checking that the object satisfies the interface, not that it extends/implements the interface explicitly.

interface ResponseBody {
  foo1: number;
  foo2: string;
  foo3: boolean;
}

function doSomething(value: ResponseBody) {
    console.log("Ok");
}

And thus:

// This will work
doSomething({ foo1: 0, foo2: "zero", foo3: false });

// This will not work
// Argument of type '{ foo1: number; foo2: string; }' is not assignable to parameter of type 'ResponseBody'.
// Property 'foo3' is missing in type '{ foo1: number; foo2: string; }'.
doSomething({ foo1: 0, foo2: "zero" });

// This will not work
// Type 'number' is not assignable to type 'string'.
doSomething({ foo1: 0, foo2: 1, foo3: false });

// This will work
const r1: ResponseBody = { foo1: 4, foo2: "four", foo3: true };

// This will not work
// Type 'string' is not assignable to type 'number'.
const r2: ResponseBody = { foo1: '4' };

// This will not work
// Type '{ foo1: number; }' is not assignable to type 'ResponseBody'.
// Property 'foo2' is missing in type '{ foo1: number; }'.
const r3: ResponseBody = { foo1: 4 };

So the basic answer is: remove <Foo>. You shouldn't need it.

Now, if the object you pass to doSomething is coming from somewhere else and is of any type, TypeScript cannot do the check. So it'll fail. Those are the cases where you'd need to add an assertion, to let TypeScript know that you know what you're doing. But that's because the object (and its fields) is not known at compile time, and therefore cannot be checked by TypeScript at all.