imho this should not be allowed, no?
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
1 messages · Page 1 of 1 (latest)
imho this should not be allowed, no?
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
@severe tulip Here's a shortened URL of your playground link! You can remove the full link from your message.
Preview:```ts
function fn<T extends Record<foo-${string}, number>>(
value: T
) {}
fn({foo: 0})```
Record<foo-${string}, number> says "all properties with key foo-${string} must have a value of number" it doesn't forbid other keys.
const foo: Record<`foo-${string}`, number> = { foo: 0 }
correctly complains though
ah... I see the problem. Yeah, you're right... any idea on how to forbid other keys in that position?
put the annotation on the parameter like you're doing there, rather than on an extensible generic
true... the problem is I need the exact structure for the return value
You can use a validator type to forbid other keys, but even then it can still be bypassed.
TS as a language does not care about excess properties.
yeah... damn. What do you mean by "validator type"?
Preview:```ts
type OnlyFooKeys<T> = keyof T extends foo-${string}
? T
: never
function fn<T extends Record<foo-${string}, number>>(
value: OnlyFooKeys<T>
) {}
fn({foo: 0})
fn({"foo-": 0})
fn({"foo-": 0, foo: 0})```
^ That's a validator type.
could also replace the never with an error message that's disjoint with the constraint (which string is)
Although it can still be bypassed, so it's best not to rely on it.
ah, didn't know that was called a "validator type"
bypass:
Preview:ts ... const x = { foo: 0, "foo-": 0 } const y: { "foo-": number } = x fn(y) // `foo` is still passed ...
yeah well that bypass is borderline malicious intent... if I explicitly omit the "invalid" property that's the devs my fault
like mentioned before, ts fundamentally allows excess keys. the error that comes up here is the exception, and it's there because usually it's a mistake, but it's technically not invalid in the type system.
it doesn't have to be malicious.
explicitly changing the type so it can be passed to a function that wouldn't accept the previous type doesn't have to be "malicious"? (Sorry if malicious is the wrong word here, english isn't my mothers tongue. I mean "bad intent", similiar to arguing in bad faith)
Preview:ts ... declare const x: Record<`foo-${string}`, number> // unknown extraneous keys const y = x as FooWithKeys<"a" | "b">; // intent: these keys are known // side effect: non-'foo-' keys are discarded from the type fn(y) // extraneous keys are still passed ...
i mean that it might not be explicitly changing the type for the function; changing the type can come up in normal circumstances
here, it's a mistake, rather than intentionally getting around the validator type
It doesn't have to be explicitly changing the type either, it could just be unintentional like:
function change(arg: { 'foo-': string }) {
return { ...arg, 'foo-': arg['foo-'].length }
}
const x = { foo: 'bar', 'foo-': 'baz' }
const y = change(x)
that changes the premise though? There's no validator type and no generics involved anymore?