-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assertion Equal bug for enums values #33
Comments
Hello @3axap4eHko, While it’s inconvenient to have two types that you know are equal being deemed not equal by Let me give you an example of why I cannot replace the implementation of In practice, if your type doesn’t involves functions, you can safely use I don't think there is a way to improve the implementation of I will add the Thanks for reaching out, I hope it helps clarify things. |
Maybe it worth to name it as |
@garronej Also I've checked with function and it errors type AssertEqual<T, U> = T extends U ? (U extends T ? true : never) : never;
type A = { f: (x?: string) => void }
type B = { f: (x: string) => void }
const resultFn: AssertEqual<A, B> = true; // Type 'boolean' is not assignable to type 'never'. can you provide any false negative example to test |
@3axap4eHko, sorry my answer was wrong the example I gave in my screenshot gave a false positive just because I didn't set the test case up properly. Here is an actual example that gives false positive: type Equals<T, U> = T extends U ? (U extends T ? true : false) : false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type A = any[];
type B = [number][];
assert<Equals<A, B>>(); // Thoses type are deemed equals with the above implementation of Equals but not with the tsafe implementation. So I think it's save to use the ExtendsEachOther implem of the Equal function as long as the types compared are guarenteed to have no |
@garronej they are equal too TypeScript playground type AssertEqual<T, U> = T extends U ? (U extends T ? true : never) : never;
type A = any[];
type B = [number][];
const resultArrays: AssertEqual<A, B> = true; |
@3axap4eHko Well they shouldn't, A and B are not the same type here. |
@garronej But they are the same type A = any[];
type B = [number][]; comparison of these types can be simplified to compare their subtypes type A = any;
type B = [number];
const resultArrays: AssertEqual<A, B> = true; types are the same if you can assign them to each other, means assert<Equals<number, any>>(false); // errors
assert<Equals<number, any>>(true); // errors
assert<Equals<any, any>>(false); // succeed
assert<Equals<any, any>>(true); // succeed Am I missing something? |
From a type theory perspective, you are correct that any value of type For the helper type |
@garronej your explanation does make sense, but this assertions does not confirm your explanation assert<Equals<any, any>>(false); // succeed
assert<Equals<any, any>>(true); // succeed UPD: Also this assertion looks like a false negative according to your explanation // @ts-expect-error
assert<Equals<number, any>>(false); |
It might seem confusing, but this is actually working as expected. There are two ways to use the import { assert, type Equals } from "tsafe/assert";
assert<Equals<A, B>>(); // This will cause a type error if A and B are not the same type. It's mainly usefull for writing type level unit test on generic functions with complex return types that get inferred from the input types. Example, I want to test my const x = (["a", "b", "c"] as const).filter(exclude(["a"]));
type Got = typeof x;
type Expected = ("b" | "c")[];
assert<Equals<Got, Expected>>(); These kinds of assert statements can be removed from the distribution build as they are solely type safeguards and do not do anything at runtime. On the other hand, you can use the import { assert } from "tsafe/assert";
type Shape = {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
};
export function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
assert(shape.radius !== undefined, "radius is required for circle");
return Math.PI * shape.radius ** 2;
case "square":
assert(shape.sideLength !== undefined, "sideLength is required for square");
return shape.sideLength ** 2;
}
} Here, the For instance, if we do this: We get type errors, and rightfully so. Using This is a very different use case, as we are actually doing something at runtime. I think what confused you is this GIF: Here, I pass I hope this clarifies things a bit. |
I consider types are equal if they are extend each other. Unfortunately it is not the case for Equal
The text was updated successfully, but these errors were encountered: