#Why the following code compiles?

80 messages Β· Page 1 of 1 (latest)

wet wraithBOT
#

@rose karma Here's a shortened URL of your playground link! You can remove the full link from your message.

stavalfi#0

Preview:```ts
function fallback<
Method1Fn extends Function,
FallbackFn extends Method1Fn

(props: {fallback: FallbackFn}) {
return function (
target: any,
propertyKey: string,
descriptor: Omit<PropertyDescriptor, "value"> & {
value?: Method1Fn
}
) {
// implement fallback logic...
return descriptor
}
}

class MyClass1 {
// I expect a compilation error because FallbackFn does not extend Method1Fn
@fallback({
fallback: (props: {x: number}): number => {
return 0
},
}) // works! why??
static method1(pros: {}): number {
...```

rose karma
#

i recreated new post. sorry im new to discord πŸ˜„

tired bluff
#

Seems like a bug to me

#

Like, this might be nonsense, but what comes to mind is that it infers Method1Fn as Function, accepts FallbackFn, then updates Method1Fn based on the later code at descriptor: but doesn't go back and apply the constraint

#

But that's just a random concept

rose karma
#

:S

tired bluff
#

!helpers

wet wraithBOT
#

:warning: Only the asker can ping helpers

tired bluff
#

Ah

#

I wonder if anyone knows about decorators, I haven't used them

#

maybe search the github issues for posts about it

rose karma
#

!helpers

lapis orbit
#

Are we focusing on the fact that fallback is defined with the signature

(props: { x: number }) => number```Meanwhile you expect it to be```ts
(props: {}) => number;```which is the same signature as the static method `method1`?
rose karma
#

how they are the same? @lapis orbit ?

type Fallback = ((prpos: { x: number }) => number)
type Original = ((prpos: { }) => number)

type isExtends = Fallback extends Original ? true: false // false -->> so why the above compiles?

lapis orbit
#

No, I'm just asking if that's what we're focusing on here.

rose karma
#

I'm trying to understand why the code compiles because the fallback does not extends method1.

tired bluff
#

if you call the function with those types passed, without the decorator pattern, it throws type error

lapis orbit
#

We can see that if we switch the type to something that fallback does not satisfy:

tired bluff
#

But fallback decorator asked for covariance not contravariance

wet wraithBOT
#
sandiford#0

Preview:```ts
function fallbackDecorator<
Method1Fn extends Function,
FallbackFn extends Method1Fn

(props: {fallback: FallbackFn}) {
return function (
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<Method1Fn>
) {
// implement fallback logic..
...```

tired bluff
#

Call the function directly and it fails

#

So unless decorators reverse the meaning of extends this is a puzzle to me

lapis orbit
#

Yeah, now I see, interesting.

rose karma
#

@lapis orbit @tired bluff should I open a issue on TS github?

tired bluff
#

Have you searched?

rose karma
#

im not sure what to search for. you guys just intirudced me to covariance and contravariance which gave me much knowledge about this subject

vagrant cobalt
#

i haven't looked closely at this yet, but perhaps it's just a type info bug with the decorator version? when you call the function directly and let the type parameters be inferred there is no type error (because Method1Fn is inferred as Function, since FallbackFn needs to be inferred first before the second call):

wet wraithBOT
#
mkantor#0

Preview:ts ... declare const g: (props: { }) => number fallbackDecorator({ fallback: f })(null, '', { value: g }) // no error

vagrant cobalt
#

another thought is maybe it is related to method bivariance, but i'd need to play around more to be sure of that

tired bluff
#

descriptor: Omit<PropertyDescriptor, 'value'> & { value?: Method1Fn }

#

In the sense that changing this changes the displayed infered type

vagrant cobalt
#

yes, but that's part of the second parameter list of the decorator function, which in "normal" calls happens too late to affect inference for Method1Fn (as in my above example)

#

i'm saying maybe that's what's happening with the decorator version too, but the type info is lying about it. just a guess though

#

it wouldn't be the first bug i've seen where type info diverges from the "real" type

tired bluff
#

Yeah well it sounds kinda similar to my theory, that the inference of Type1 is late, and it does not retroactively apply the constraint to Type2

#

But that still feels like a bug

#

If it just didn't infer Type1 based on the return function type then I'd understand it

vagrant cobalt
#

yeah

#

this makes me lean away from that theory a bit, though:

wet wraithBOT
#
mkantor#0

Preview:ts ... const fallback2 = fallback({ fallback: (props: {x: number}): number => { return 0 }, }) class MyClass2 { @fallback2 static method1(pros: {}): number { return 1 } } ...

vagrant cobalt
#

my earlier thought would imply that fallback2 would act the same way as the original version (except perhaps in type info)

#

can you decorate a non-method function? i'm trying to figure out how to test whether method bivariance is relevant here

#

i don't do OOP often enough to know these things, let alone use decorators

tired bluff
#

I can answer OOP questions, but I haven't used decorators except for briefly trying them out. And that might have been an older specification.

#

Your example doesn't work because it can't infer the correct function type to return when called prematurely

vagrant cobalt
#

oh right i guess it's complaining about the return there, not the parameters. if fallback is changed to return void from its second call then fallback2 is happy

wet wraithBOT
#
sandiford#0

Preview:```ts
function fallback<
Method1Fn extends Function,
FallbackFn extends Method1Fn

(props: {fallback: FallbackFn}) {
return function (
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<Method1Fn>
) {
// implement fallback logic..
...```

tired bluff
#

FYI
Omit<PropertyDescriptor, 'value'> & { value?: Method1Fn }
can be changed to
TypedPropertyDescriptor<Method1Fn>

rose karma
#

@tired bluff why does this works?

function fallback<Method1Fn extends Function, FallbackFn extends Method1Fn>(props: { fallback: FallbackFn }) {
return function(target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<Method1Fn>
) {
// implement fallback logic...
return descriptor
};
}

declare const fallbackFn:(prpos: { x: number }) => number
declare const method1Fn: (prpos: { }) => number

const f1 = fallback({fallback:fallbackFn})(1,"2",{value:method1Fn})

tired bluff
#

as descriptor is meaningless when called as a function, and method1Fn is not passed to referred to anywhere

rose karma
#

ho but why does method1 inference not working?

interface TypedPropertyDescriptor<T> {
enumerable?: boolean;
configurable?: boolean;
writable?: boolean;
value?: T;
get?: () => T;
set?: (value: T) => void;
}

tired bluff
#

You passed method1Fn to the function created

wet wraithBOT
#
sandiford#0

Preview:```ts
...
const newFunc = fallback({fallback:fallbackFn}) // T1 inferred as Function

newFunc(1,"2",{value:method1Fn}) // can't go back in time and infer T1 based on a later call```

rose karma
#

I thought that because it's in the same expression, ts will infer it. thanks πŸ™‚

tired bluff
#

it's not the same expression, you are just doing 2 separate things on one line πŸ™‚

wet wraithBOT
#
sandiford#0

Preview:```ts
function fallback<
/* removed T1 */ FallbackFn extends Function

(props: {fallback: FallbackFn}) {
return function <Method1Fn extends Function>( // move T1 to here
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<Method1Fn
...```

tired bluff
#

just an example, not really useful

#

inner expression outer expression

#

they are nested technically. But still one executes before the other.

rose karma
#

is it clear and precise?

rose karma
#

I want to say thank you for all the help so far! I learned alot in the process! @tired bluff @lapis orbit

rose karma
#

@tired bluff @lapis orbit I got a response in the GitHub but I can't fully understand the problem. Can you help me understand? Is there a way to get a compilation error in the class?
https://github.com/microsoft/TypeScript/issues/58354#issuecomment-2085328360

GitHub

πŸ”Ž Search Terms decorator, function, type-safety, @, covariance, contravariance πŸ•— Version & Regression Information all versions ⏯ Playground Link https://www.typescriptlang.org/play/?experimenta...

tired bluff
#

He says that the arg is alllowed to the bi-variant

#

so co or contra variant

#

looser check

#

I don't know how you enforce it, seemingly with that type TS will always say that FallbackFn extends Method1Fn if its a subtype or a supertype

#

Maybe if instead of having Method1Fn extends Function you have MethodArgs and MethodReturn and TypedPropertyDescriptor<(...args: MethodArgs) => MethodReturn> and FallbackFn extends (...args: MethodArgs) => MethodReturn

#

Pulling the args and return type, then building a normal function shape with them might just work.