-
-
Notifications
You must be signed in to change notification settings - Fork 59
Excess property checks for destructured arguments #359
Comments
you are asking for if you want to make that internal logic same as external interface in hegel you can write this function stringifyUser({ name, email }): string {
return `${name} (${email})`
} and inferred type will be what you wanted
you don't need to annotate anything type is derived from usage this can be solved with type User = {
id: number;
name: string;
email: string;
}
function stringifyUser({ name, email }: AutoPick<User>): string {
return `${name} (${email})`
} so it's same as i don't see this implemented in typescript or hegel anytime soon but it can be done |
Is it "internal logic" though? I mean, it's part of the function declaration - it's the closest thing we have to a type-hint in JS, in practice saying, "the argument is an object with these properties". I think it's a matter of description. The way I see it, the fact that an object is required, and the fact that the object must have certain properties, that's not just internal logic - it's part of the function's public interface. These are declarations with static meaning. It's a close analog to named arguments, as well as (effectively) a shallow run-time type-check. Adding new properties to a destructured argument is not that different from adding new arguments to a function - unless they're optional, calling the function without those arguments should be an error. And vice versa, calling the function with extra arguments is permitted, just as calling the function with extra properties to destructured arguments is permitted. But not required. Why would it be? 🤔 |
Let me reverse the question: Where do you see a case for functions that demand properties they can't access or use? 🤷♂️ |
i understand where confusion comes from but destructuring is not part of function interface it's just a suger syntax // original
function stringifyUser({ name, email }) {
return `${name} (${email})`
}
// desugering pass 1
function stringifyUser(obj) {
const { name, email } = obj
return `${name} (${email})`
}
// desugering pass 2
function stringifyUser(obj) {
const name = obj.name
const email = obj.email
return `${name} (${email})`
} this is what js engines do on AST level before executing that function so it is functions internal logic and has nothing to do with types |
btw they can access it type User = {
id: number;
name: string;
email: string;
}
function stringifyUser({ name, email }: User): string {
return `${name} (${email}) - ${arguments[0].id}`
} |
I promise, I'm not confused. 😄 What you explained is just language implementation details. How the language or some transpilers implement this feature internally is rather moot. I'm talking about what the language feature means to programs and programmers in a practical sense. If you asked for
I also noted that in my original post:
Can you think of a practical example where this would be problematic? Let's get to the bottom of this. 😄 If a function declaration explicitly declares a dependency on a subset of properties of an object: When, how or why is it useful to get a resulting function type that validates statically for the presence of properties that the function does not ask for or expect at run-time? |
you are not asking for you are asking for if you want usage based interface on functions hegel does do that but in that case you need to remove annotation for
i have a function that's asking for |
That's a matter of interpretation. If you have a function that accepts So the way I see it, it's not internal to the function - it's part of it's public signature. I mean, if you wanted to be really pedantic, you could argue that the whole argument list is internal to the function, right? Technically, a function
In a nominally-typed language, yes - but in a structurally-typed language? What you really asked for is "object matching this shape" - and then explicitly selected certain specific properties from that shape. If we have these facts, why would we type-check properties you didn't ask for? Unless you also asked for
That sounds more like an imagined future problem than a practical problem. If you anticipate a future need for a full Anyhow, I think I've made my point - I think this feature solves a problem, but no one seems to agree with me. 😅 |
I've struck up a longer discussion about type-checking of destructured arguments in a Typescript issue here and I was wondering if I could get your opinion on this matter.
Given this example:
Would it be reasonable to infer the argument type as
Pick<User, "name" | "email">
?I mean, the underlying JS does not require an object with an
id
property, so why should this be required by the type-checker?Hegel tends to be about accuracy, and I had actually hoped to find it already working like this.
But as they pointed out in the thread, only 4 people have ever asked for this feature, so maybe it's just not something anybody notice, cares or thinks about. From my perspective, pretty simple: I want functions with as few dependencies as are actually required.
And sure, I could type out
Pick
types all day, but why wouldn't the type-checker just check the types that are actually relevant to the function, instead of demanding properties that the function can't actually access?That's how it works in JS, where destructuring arguments is the closest thing we have to a type-hint.
What do you think? 🤔
The text was updated successfully, but these errors were encountered: