menu
announcement

Spectrum is now read-only. Learn more about the decision in our official announcement.

TypeScript

A place to talk about TypeScript.

Channels
Team

Classical case of "Type 'string' is not assignable to type..."

August 12, 2020 at 12:35am

Classical case of "Type 'string' is not assignable to type..."

August 12, 2020 at 12:35am
I have a really simple case. Let's say I have a function that take a configuration. The configuration is a plain JS object that can take any key. The values can be a boolean, a function and a string that represent an alias of another key in the current configuration to be valid.
The problem I have is that, everything is correct if I pass the configuration directly to the function. But, I have the classical "Type 'string' is not assignable to type..." when I try to pass an "external" configuration.
I can fix this by typecasting the "external" configuration, but I want to avoid typecasting as much as possible.
Did anyone have an idea how to achieve this without typecasting?
import { PickByValue } from "utility-types"; // see https://github.com/piotrwitek/utility-types#pickbyvaluet-valuetype
type Config<T = never> = {
[k: string]: boolean | Function | T;
};
const fn = <
T extends Config<K>,
K extends keyof PickByValue<T, boolean | Function>
>(
config: T
) => "";
// Expected behavior
fn({
foo: true,
bar: () => "",
baz: "foo", // valid: the "foo" key is listed in the current object and is a boolean
});
// Expected behavior
fn({
foo: true,
bar: () => "",
baz: "qux", // invalid: the "qux" key is not listed in the current configuration object
});
// Expected behavior
fn({
foo: true,
bar: () => "",
baz: "foo", // valid: the "foo" key is listed in the current object and is a boolean
qux: "baz", // invalid: the "baz" is an alias
});
// Unexpected behavior
const outsideConfig = {
foo: true,
bar: () => "",
baz: "foo", // valid: the "foo" key is listed in the current object and is a boolean
};
fn(outsideConfig); // invalid: Type 'string' is not assignable to type 'boolean | Function | "foo" | "bar"'

August 12, 2020 at 3:00pm
baz: "foo", // valid: the "foo" key is listed in the current object and is a boolean
- };
+ } as const;
Playground Link
Hey . Thank for your answer. But this is exactly what I want to avoid. Type casting (aka type assertion). There's no way to do this directly into the fn or Config definitions directly, so the library users don't have to do typecasting everytime they want to interact with the function?
It isn't a type assertion though: the syntax is ambiguous. as const is not a type assertion. It is actually narrowing down the type to literals, so instead of bar: string the type is bar: "foo". Read about it :)
There's also a way to do what you seek, but I don't remember how exactly. Search for "type narrowing".