-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Closed
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Milestone
Description
I was trying to add type safety to some model i have and it seems to sometimes work in a weird way.
Code
export enum FilterType {
String = 'string',
Number = 'number',
Boolean = 'boolean'
}
export interface FilterTypeToLiteralTypeMap {
[FilterType.String]: string;
[FilterType.Number]: number;
[FilterType.Boolean]: boolean;
}
export type AllowedFilterTypeLiteralTypes = FilterTypeToLiteralTypeMap[FilterType];
export type Values<T> = T[keyof T];
export type FilterTypeFromLiteralType<
T extends AllowedFilterTypeLiteralTypes
> = Exclude<
Values<
{
[key in FilterType]: FilterTypeToLiteralTypeMap[key] extends T
? key
: never;
}
>,
never
>;
export interface Filterable<T extends AllowedFilterTypeLiteralTypes> {
isFilterable: true;
filterType: FilterTypeFromLiteralType<T>;
}
export interface NotFilterable {
isFilterable: false;
}
export type FilterableTrait<T extends AllowedFilterTypeLiteralTypes> =
| Filterable<T>
| NotFilterable;What i did here is created an enum and mapped each entry to a literal type. That way when using the FilterableTrait interface it can be either Filterable or NotFilterable and if filterable the FilterType is decided based on the given type.
Some usages that work
// Errors correctly
export const a: FilterableTrait<string> = {
isFilterable: true,
filterType: FilterType.Number
};
// Works correctly
export const b: FilterableTrait<string> = {
isFilterable: true,
filterType: FilterType.String
};
export const c: FilterableTrait<string> = {
isFilterable: false,
filterType: FilterType.String // Errors correctly
};
// Errors correctly
export const d: FilterableTrait<string> | FilterableTrait<number> = {
isFilterable: true,
filterType: FilterType.Boolean
};
// Works correctly
export const e: FilterableTrait<string> | FilterableTrait<number> = {
isFilterable: true,
filterType: FilterType.Number
};The problem starts when i do this
export const f: FilterableTrait<string> | FilterableTrait<number> = {
isFilterable: false,
filterType: FilterType.Number // No error? Auto complete doesn't add this as an option but it compiles
};A few notes here:
- If i change
Filterableto befilterType: FilterTypeFromLiteralType<string>instead offilterType: FilterTypeFromLiteralType<T>it works... it doesnt work just when i use the generic type. - I know i can use
export const g: FilterableTrait<string | number> = {
isFilterable: false,
filterType: FilterType.Number
};and it will work but my actual use case requires me to use something like
export const h:
| ({ type: '1' } & FilterableTrait<string>)
| ({ type: '2' } & FilterableTrait<number>) = {
type: '2',
isFilterable: true,
filterType: FilterType.Number
};Which means i can't use the above
Expected behavior:
Compilation error
Actual behavior:
Compiles successfully
Playground Link: http://www.typescriptlang.org/play/?ts=3.7-Beta&ssl=31&ssc=3&pln=17&pc=1#
Metadata
Metadata
Assignees
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed