#Why am I allowed to assign variable here, that doesn't implement the whole interface?

24 messages · Page 1 of 1 (latest)

sullen archBOT
#
uriziel#0

Preview:```ts
type AProps = {
onClose: () => void
}

type BProps = {
onSave: () => void
onClose: () => void
}

function A({onClose}: AProps) {}
function B({onSave, onClose}: BProps) {}

const Zzz: (props: BProps) => void = A```

errant schooner
#

@next meadow Functions are allowed to ignore parameters they are passed.

next meadow
#

But is there any way to require it (like React does)

errant schooner
#

There isn't.

#

(I'm not sure what part of this would be required on react's side either: a component can ignore props that are passed to it too)

next meadow
#

I'm fine with ignoring the props, but I'd like the interface of that function to be explicit :X

i.e. in React I cannot pass <a something="1" />
but with my example, it would totally pass

errant schooner
#
type AProps = {
    onClose: () => void;
}

type BProps = {
    onSave: () => void;
    onClose: () => void;
}


const A = (props: AProps) => {
    return <div onClick={props.onClose} />
}

// Okay, BProps includes all the props that A needs
const Z: React.ComponentType<BProps> = A;

const example = <Z onSave={() => {}} onClose={() => {}} />
#

This is the JSX equivalent code and it's fine, too, even though A is being passed a onSave prop that it doesn't use.

#

With the original TS version, you'll get an error if you write:

function A({onClose}: AProps) {}

A({ onClose: () => {}, onSave: () => {} })

... but not if you widen the type with Zzz.

next meadow
#

Yeah, this is exactly my case, and if I switched the last line to:

const example = <A onSave={() => {}} onClose={() => {}} />

I'd get an error

errant schooner
#

Right, see my last message, you still get that with your original version, too.

next meadow
#

So my question is, am I somehow able to retain that behaviour ("A is invalid here") when reasinging to a different variable

errant schooner
#

No, that's really not possible.

next meadow
#

Fair

#

thanks

errant schooner
#

If TS errored on stuff like this, then [1,2,3].map(x => x + 1) would be an error.

#

Because the callback function is actually provided three arguments:

[1,2,3].map((x, i, arr) => x + 1)
eager pulsar
#

you could use satisfies rather than a type annotation:

const Zzz = A satisfies (props: BProps) => void;

satisfies won't widen the inferred type, just check that it conforms to the target type

next meadow
#

satisfies doesn't seem to work in my example either, resulting in the same behavior

errant schooner
#

Yeah, it doesn't mean that A wouldn't be assignable to Zzz's type.

#

It's a way of assigning A to Zzz without 'forgetting' the specific structure of A.

eager pulsar
#

maybe i misunderstood, but you get an excess property warning here if you use satisfies:

sullen archBOT
#
type AProps = {
    onClose: () => void;
}

type BProps = {
    onSave: () => void;
    onClose: () => void;
}


function A({ onClose }: AProps) {}
function B({ onSave, onClose }: BProps) {}

const Zzz1: (props: BProps) => void = A;
Zzz1({ onClose: () => {}, onSave: () => {} })

const Zzz2 = A satisfies (props: BProps) => void;
Zzz2({ onClose: () => {}, onSave: () => {} })
//                        ^^^^^^
// Argument of type '{ onClose: () => void; onSave: () => void; }' is not assignable to parameter of type 'AProps'.
//   Object literal may only specify known properties, and 'onSave' does not exist in type 'AProps'.
next meadow
#

Okay, your example seems to make sense to me now