Skip to content
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

Change return type of Object call signature to unknown (currently any) #57379

Open
szacskesz opened this issue Feb 11, 2024 Β· 4 comments
Open

Change return type of Object call signature to unknown (currently any) #57379

szacskesz opened this issue Feb 11, 2024 Β· 4 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@szacskesz
Copy link

πŸ”Ž Search Terms

ObjectConstructor, Object, assignable, function

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about 5.3.3

⏯ Playground Link

https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtVJxwAowAjALniIEp4BeAPngGcMYtUBzaygNxyzAA3ACgRiQjSEB6afECg5PACiADwAO4DCGDw4zZBAyUQMGDhjjJNekwBEt6jLmLVGzNt0h9hyqhzwTMwsJYmtGeFQDCEdZBWV1TQ89AyMA03NLYgBvAF9qWJcE9x1knzSgzKIAZTYObic410SSrxTff0CMkKIAOWQAW1ITGOd4ty0W71TOi0qAeVIAK00R+EAZcjHmzynjdIsgA

πŸ’» Code

declare function foo(cb: () => string): void;

foo();// βœ… Expected result: error
foo(() => "");// βœ… Expected result: no error
foo(() => null);// βœ… Expected result: error
foo({})// βœ… Expected result: error
foo(String);// βœ… Expected result: no error
foo(Number);// βœ… Expected result: error

foo(Object);// ❌ Expected result: error

πŸ™ Actual behavior

Object (ObjectContructor) is assignable to any function, because it has a callable signature with return type of any.

This lets the developers to use Object where only a properly typed function would be allowed, which can lead to unexpected runtime errors.

I know this is a pretty edge case, but in my opinion it's worth looking into.

πŸ™‚ Expected behavior

Object is not assignable to functions with different return type

Additional information about the issue

No response

@RyanCavanaugh
Copy link
Member

It's declared as having a (): any; call signature since the definition predates the existence of unknown/object and types deriving explicitly from Object are obviously free to be ~anything. I don't see much upside in changing that -- this is basically the only observable effect of that.

@szacskesz
Copy link
Author

Wouldn't it be better to have an unknown return type for the callable signature (): unknown;, and let the descendants specify it more if they want?
To me it makes more sense, and it would be consistent with the Do's and Don'ts you wrote about declaration files too:

In cases where you don’t know what type you want to accept, or when you want to accept anything because you will be blindly passing it through without interacting with it, you can use unknown.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Feb 13, 2024
@RyanCavanaugh RyanCavanaugh changed the title Object (ObjectConstructor) is wrongfully assignable to any function Change return type of Object call signature to unknown (currently any) Feb 13, 2024
@rotu
Copy link

rotu commented Feb 21, 2024

I think object would be more appropriate than unknown. The return type of Object is known to not be a primitive type and can be used where an Object is required, e.g. as the key of a WeakMap. See also e.g. #57000

Also, it's confusing that Object() and new Object() have different return types. Not sure if intentional.

@matAtWork
Copy link

Can I suggest more strictly typing this as:

type Boxed<T> =
    T extends number ? Number :
    T extends string ? String :
    T extends boolean ? Boolean :
    T extends bigint ? BigInt :
    T;

declare global {
    interface ObjectConstructor {
          assign<T extends {}, U>(target: T, source: U): Boxed<T> & U;
          new <T>(value?: T): Boxed<T>;
          <T>(value: T): Boxed<T>;
      }
}

This mirrors how all mainstream JS engines actually handle primitive types passed to Object(p) and Object.assign(p,q), and avoids a problem with type checking here: samchon/typia#911 (comment)

It's more than possible my basic augmentation is incomplete, but any is certainly a poor choice for modelling the actual behaviour of these expressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants