Add constraints to type parameters of `Has`
Because typescript doesn't support strict version of Extract for union types. (https://github.com/microsoft/TypeScript/issues/31474)
How about add constraints to type parameters of Has?
current behavior
assert<Has<'a' | 'b', 'a'>>(true); // true.
assert<Has<'a', 'a' | 'b'>>(true); // also true. Can 'a' has 'a' | 'b' ?
assert<Has<string | number, number>>(true); // true.
assert<Has<number, string | number>>(true); // also true. Can `number` has `string` | `number` ?
Suggestion
/**
* Checks if type `T` has the specified type `U`.
*/
export declare type Has<T, U extends T> = IsAny<T> extends true ? true : IsAny<U> extends true ? false : Extract<T, U> extends never ? false : true;
assert<Has<'a' | 'b', 'a'>>(true); // true.
assert<Has<'a', 'a' | 'b'>>(true); // Type '"a" | "b"' does not satisfy the constraint '"a"'.
assert<Has<string | number, number>>(true); // true.
assert<Has<number, string | number>>(true); // Type 'string | number' does not satisfy the constraint 'number'
@younho9 have you taken a look at the type definition of Has? First argument is the test subject. Second argument is the assertion. Perhaps a more concrete example would help here? Say I have a global type of all possible languages and a check type/function (like a type guard) that narrows to a specific one. Should this not be truthy?
const allowedLocales = [
'us-en',
'ca-en',
'ca-fr',
] as const
function isAcceptedLocale<T extends typeof allowedLocales[number]>(locale:T): locale is T {
return allowedLocales.includes(locale)
}
const myLocale = 'us-en'
if(isAcceptedLocale(myLocale)) {
assert<Has<typeof myLocale, typeof allowedLocales[number]>>(true)
}
else {
assert(true)
}
Based on your first example I think it should be true, because I'm trying to assert the typeguard will respond to the expected structure.
However, based on your second example, I can see the need for more strictness given the typical testing structure of expect(result).is.a.(value). The problem with example 2 is that while it works for Assert, which has the types pre-specified in the definition, TS's ability to contextualize beyond the current scope (type/function definition) is pretty limited. See this example where I update Has to more appropriately ensure T extends U (cascade effect to other functions possibly creating a maintenance nightmare because the T extends U effect must permeate every single definition). However, assuming this lib dealt with that overhead it would get the expected result (1 fails, wrong order; 2 passes correct order)
Not saying there's not a solution, but it might need an entire rewrite of the lib, if it is possible at all.
Whelp. I played around a little more and looked at more examples in the current lib (and other issue comments). I think I've found a solution to ensure the interface of Has matches what one would expect a typical testing interface would look like.
If we do this I noticed a few other functions seem to not really follow the "result === expectation" (left to right) thinking, so a PR should include tweaks to their direction as well. Not to mention this would most certainly be a breaking change (major bump)