I have some interface
ITestInterface {
foo: string;
}
I'd like to pass an instance of this interface as an argument to a function. The function will take any object type, so it does not type check on it's own. In order to make sure an object is of the correct type, I can use storage:
const passMe: ITestInterface = { foo: "bar" };
someFunction(passMe);
But I'd like to have a way to create the argument inline, while still doing type checking.
// made up example syntax
someFunction({ foo: "bar" } istype ITestInterface);
Is there a nice way to something like the example above inline?
I've tried using as, but it doesn't limit the type. For example, the following is valid.
someFunction({ foo: "bar", hello: true } as ITestInterface);
Another thing I can do in this instance is modify someFunction
to have templating, but it's not what I'd consider a great solution. I won't always have this privilege.
someFunction<TYPE>(arg: TYPE) {
// modify function definition
}
someFunction<ITestInterface>({foo: "bar"});
The specific feature you're looking for, something like "type annotations for arbitrary expressions", doesn't exist in TypeScript. There is an open suggestion for it currently marked as "needs proposal", so you might want to give it a 👍 or describe your ideas if they are compelling and different from what's already in there. But it doesn't look to me like anyone's working on it, so I wouldn't hold my breath if I were you.
There are several ways to go here, each with their own issues.
As you've seen, the easiest thing to do is to use a type assertion. This works to prevent you from passing in a completely unrelated type:
// assertion
someFunction({ foo: "bar" } as ITestInterface); // okay as expected
someFunction({ unrelatedThing: 1 } as ITestInterface); // error as expected
It also allows extra properties (which is still sound and type safe, an object of type ITestInterface
isn't guaranteed not to have other properties... it might surprise you because you expect excess property checking, but those only happen sometime):
someFunction({ foo: "bar", hello: true } as ITestInterface); // okay by design,
// excess properties are allowed
But the big dealbreaker here is that type assertions let you unsafely narrow types, so the following will not be an error:
someFunction({} as ITestInterface); // no error ?! assertions also NARROW types
The other way you could go would be to create a helper function called isType
like this:
// helper function
const isType = <T>(x: T) => x;
This behaves almost exactly as you'd like:
someFunction(isType<ITestInterface>({ foo: "bar" })); // okay as expected
someFunction(isType<ITestInterface>({ unrelatedThing: 1 })); // error as expected
someFunction(isType<ITestInterface>({ foo: "bar", hello: true })); // error as you want
someFunction(isType<ITestInterface>({})); // error as everyone wants
But, as you said, it might not be worth it to you. Most runtime engines will happily inline functions like x => x
so I wouldn't think it's a performance issue. But it might be an elegance issue, which is up to you.
Anyway, those are the best I can do. Hope that helps. Good luck!