Introduce Extends
danieldietrich opened this issue ยท 11 comments
Hi Joseph,
there is the use-case of testing if only certain TS types cover a special type definition.
// a type that reflects functions and arrow functions
type Fn<A extends any[] = any[], R = any> = (...args: A) => R;
I need to ensure that only and only function cover the Fn
type. E.g. type Fn = any
would cover functions but also other types that are not intended to be expected. The existing type Equals
is unsuitable for testing if types are inside or outside of a domain (read: a set of valid types).
Introducing an Extends
type to tsafe would make it possible to assert that Fn
covers the types we intend to cover and to ensure that other types are not covered (as expected).
๐๐๐
type Extends<T1, T2> = T1 extends T2 ? true : false;
By having the following unit tests, we would ensure that the Fn
type does not cover more types than expected.
describe('type Fn', () => {
function fn() {}
class A {}
// "sunny path": these would sill succeed if `type Fn = any`
it('should cover () => any', () => { tsafeAssert<Extends<() => any, Fn>>(); });
it('should cover () => void', () => { tsafeAssert<Extends<() => void, Fn>>(); });
it('should cover (...args: any[]) => any', () => { tsafeAssert<Extends<(...args: any[]) => any, Fn>>(); });
it('should cover typeof fn', () => { tsafeAssert<Extends<typeof fn, Fn>>(); });
// "correctness": these would fail if `type Fn = any`
it('should not cover undefined', () => { tsafeAssert<Not<Extends<undefined, Fn>>>(); });
it('should not cover null', () => { tsafeAssert<Not<Extends<null, Fn>>>(); });
it('should not cover boolean', () => { tsafeAssert<Not<Extends<boolean, Fn>>>(); });
it('should not cover number', () => { tsafeAssert<Not<Extends<number, Fn>>>(); });
it('should not cover string', () => { tsafeAssert<Not<Extends<string, Fn>>>(); });
it('should not cover array', () => { tsafeAssert<Not<Extends<any[], Fn>>>(); });
it('should not cover object', () => { tsafeAssert<Not<Extends<object, Fn>>>(); });
it('should not cover class', () => { tsafeAssert<Not<Extends<A, Fn>>>(); });
});
If you also think that Extends
would be a great fit for tsafe, I would be happy to create a PR!
What do you think?
Thanks!
Hi @danieldietrich,
Thank you for sumbitting this proposal I agree, Extends
would be a usefull helper.
I usually use the inline version in my code like:
assert<A extends B ? true : false>();
But I guess it's less readable than having an helper an few pepoles think about doing this.
Anyway, if you want tu submit a pr for Extends
I'll be happy to merge.
Most important thing it to document it.
I send you an invite at mail@danieldietrich.dev for the gitbook.
Best regards
Awesome, thank you! I will prepare a PR!
Hey @garronej, when writing the tests for the PR, I stumbled upon this:
// this assertion holds
assert<never>();
Is that expected behavior or a bug?
Regarding never
, the following holds but works not well together with assert
:
type Extends<A1, A2> = A1 extends A2 ? true : false;
assert<Equals<Extends<never, any>, never>>(); // assert<never>() would hold but might not be expected
assert<Equals<Extends<never, 1>, never>>(); // dito
assert<Equals<Extends<any, never>, boolean>>(); // assert<boolean>() would create a compiler error
A more appropriate definition would be
type Extends<A1, A2> =
StrictEquals<A1, never> extends true ? StrictEquals<A2, never> :
StrictEquals<never, A2> extends true ? false :
A1 extends A2 ? true : false;
assert<Equals<Extends<never, 1>, false>();
assert<Equals<Extends<1, never>, false>();
assert<Equals<Extends<never, never>, true>();
This is what I would expect because the only purpose of Equals
and Extends
are to be used with assert
and therefore need to return true
or false
. Returning never
does not really make sense for assertions.
What do you think?
Hi @danieldietrich,
Thank you for folloing up with a PR and thank you for your enthusiasm!
This is what I would expect because the only purpose of Equals and Extends are to be used with assert and therefore
need > to return true or false. Returning never does not really make sense for assertions.
On the top of my mind I'd say this is a bug but there might be some edge cas I don't remembrer.
This is what I would expect because the only purpose of Equals and Extends are to be used with assert and therefore
need to return true or false. Returning never does not really make sense for assertions.
Agree 100%
TIL about distributive conditional types and that never
is an empty type union. See this Twitter discussion. With that in mind, I understand that there is no TS bug.
Agree 100%
Yep, makes absolutely sense. I will make Extend
sound.
Merged and a new release has been published v1.1.0
.
Thank you for your contribution!
Most important thing it to document it.
I send you an invite at mail@danieldietrich.dev for the gitbook.
Hi @garronej, could you please send a new gitbook invite to my address cafebab3@gmail.com ?
(I will update my public profile)
Thx!
Done, thank you!
BTW, i'm using extends already, in https://github.com/codegouvfr/react-dsfr
Verny nice! Thank you :)