-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Inferred type union allowed to assign to an uncomparable type #33657
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
Comments
You should not be using casts here; you get the correct behaviour if you use a type annotation instead: const employees2: Employee[] = [
{ salary: 3456 },
{ salar_y: 3456 }, // error
];
const employees3: Employee[] = [
{ salar_y: 3456 }, // error
{ salary: 3456 },
];
const employees4: Employee[] = [
{ salary: 3456 },
{ salar_y: 3456 }, // error
{ salary: 3456 },
]; |
Casts, or type assertions, are a way to tell the compiler: "Sshhh.. trust me.. I am right..". But you weren't. |
Yes, I know. |
They don't have to be subtypes. Just have properties that sufficiently overlap, in general. |
This is working as intended. Type assertions are allowed to narrow values to subtypes, which is what's happening here. Even when type Employee = {
salary: number;
};
type Employe_e = {
salar_y: number;
};
let justEmploye_es = [{ salar_y: 3456 }]; // Array<Employe_e>
let justEmployees = [{ salary: 3456 }]; // Array<Employee>
let mixedBag = [{ salary: 3456 }, { salar_y: 3456 }]; // Array<Employee | Employe_e>;
let mixedBagIsASupertypeOfBoth: typeof mixedBag;
mixedBagIsASupertypeOfBoth = justEmploye_es; // okay
mixedBagIsASupertypeOfBoth = justEmployees; // okay
let employees = mixedBag as Array<Employee>; // legitimate narrowing, no error
let notEmployees = justEmploye_es as Array<Employee>; // neither widening nor narrowing, error I welcome all your various hand emojis. Yes, even that one. 👌✌🤞🖐☝👇👈👉🖖🤘 |
It is, at the moment... But should it? =P const a : string[] = ["hi"];
const b : (string|number)[] = a;
b.push(123);
console.log(a[1]); //not a string /off-topic |
@jcalz is correct. There has to be some line between "all assertions are allowed" and "the only allowed assertions are ones you don't need in the first place". In spirit this is similar to #19541, but changing the behavior to the proposed one would introduce another inconsistency between an array literal and an alias to that same literal. I would be curious about the original scenario (or if this was a "I noticed something" bug); in practice downcasts of literals should be exceptionally rare. Whatever motivated you to write that in the first place is something of interest. |
@jcalz thanks for the explanation! Didn't see that 😶it is a subtype, but I do find that confusing. @RyanCavanaugh sure I understand. The usecase is: I'm trying to find a simple way to fake data for unit tests for data processing. I've got objects with lots of properties (20+) that define the original One attempt was to do |
type Employee = {
salary: number,
propWeDoNotCareAbout : string,
}
function mockEmployees<K extends keyof Employee>(
arr : Pick<Employee, K>[]
) : Employee[] {
/**
* This is an unsafe cast but should serve the use case
* well enough.
*/
return arr as Employee[];
}
const employees : Employee[] = mockEmployees<"salary">([
{ salary: 3456 },
{ salar_y: 3456 }, // this line should fail, it fails
{ salary: 3456 },
]);
You should change those functions to accept Instead of, function calculateMonthlySalary (e : Employee) {
return e.dailySalary * 20;
} Use, function calculateMonthlySalary (e : Pick<Employee, "dailySalary">) {
return e.dailySalary * 20;
} |
Looks to me like you want to create exact subsets of For the sake of posterity: if you had put the use-case in the original example you would have received more useful feedback immediately. |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
TypeScript Version: 3.6.3
Search Terms: "as keyword", "union types"
Code
Expected behavior:
Error thrown:
Type '{ salar_y: number; }' is not comparable to type 'Employee'
. It should fail because{ salar_y: 3456 }
is incomparable to{ salary: number }
And it does fail forvar e = { salar_y: 3456 } as Employee
Actual behavior:
Compilation passes.
Playground Link
Any usecase where this is harmful? Consuming this hardcoded array injects
undefined
, where a number was expected, resulting in aNaN
in runtime. I'd expect TypeScript not to allow that.The text was updated successfully, but these errors were encountered: