The reason your example isn't working is because this:
type FilterOption = FilterOptionBase<string>
...is passing string into FilterOptionBase, essentially doing this:
type FilterOptionBase = {
name: string;
values: string[];
multiselect: boolean;
isBoolean?: boolean;
valuesMap?: {
[key in string]: string | boolean;
};
};
So to type it correctly, you'd need to pass in the list of strings you'd expect when using the type, like this:
const myBadFilter: FilterOptionBase<"Online" | "Offline"> = {
name: 'Status',
values: ["Online", "Offline"],
multiselect: false,
valuesMap: {
Online: true,
NotSure: false // <------ errors now!
}
}
However, I think what you're actually trying to do is use the FilterOptionBase type as a template of sorts for all kinds of different objects, and the unfortunate part is that there's no way to do it that way. You can't have a type that is both reading from itself and constraining itself.
But you can access the values on an object that's passed into a function, so here's a workaround that I've used in the past to great effect:
const filterFactory = <K extends string>(v: FilterOptionBase<K>) => v
const myBadFilter = filterFactory({
name: 'Status',
values: ['Online', 'Offline'],
multiselect: false,
valuesMap: {
Online: true,
NotSure: false, // <------ it's an error!
}
})
"Also" Answer
Yes, it's possible to change it based on isBoolean being true. You'll just need to do a union:
type foo = {
isBoolean: true
value: boolean
} | {
isBoolean: false
value: string
}
Though it does get a little trickier because of the workaround you have to do with the factory function. Here it is:
type FilterOptionBase<
K extends string,
B extends boolean | undefined,
> = B extends true
? {
name: string
values: K[]
multiselect: boolean
isBoolean: B
valuesMap?: {
[key in K]: boolean
}
}
: {
name: string
values: K[]
multiselect: boolean
isBoolean?: B
valuesMap?: {
[key in K]: string
}
}
const filterFactory = <K extends string, B extends boolean | undefined>(
v: FilterOptionBase<K, B>,
) => v
const myBadFilter = filterFactory({
name: "Status",
values: ["Online", "Offline"],
multiselect: false,
isBoolean: true,
valuesMap: {
Online: true,
Offline: "foo", // <------ also an error!
},
})
const myOtherBadFilter = filterFactory({
name: "Status",
values: ["Online", "Offline"],
multiselect: false,
isBoolean: false,
valuesMap: {
Online: true, // <------ also an error!
Offline: "foo",
},
})