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

Suggestion: Deprecate/remove AnyFunc #92

Open
Retsam opened this issue Mar 12, 2019 · 1 comment
Open

Suggestion: Deprecate/remove AnyFunc #92

Retsam opened this issue Mar 12, 2019 · 1 comment

Comments

@Retsam
Copy link
Collaborator

Retsam commented Mar 12, 2019

A bit of a tangent on the discussion of #89 and #90: I'm not sure how useful AnyFunc is as a whole, nowadays.

I'm skimming through our codebase, and all of the usages of AnyFunc I see are either somewhat wrong, or else looser than they should be - (due to better tuple type support). The two big categories I'm seeing are:

  1. AnyFunc<T> where () => T would be more appropriate, (or AnyFunc where () => void would be more appropriate)

For example:

function call<T>(func: AnyFunc<T>): T {
    return func()
}

This is a bit dangerous, because:

function loud(x: string) {
    return x.toUpperCase();
}
call(loud); // Compiles, but runtime error
  1. Functions that use AnyFunc that unnecessarily return (...args: any[]) where a more specific set of arguments would be appropriate:
function wrap<T>(func: AnyFunc<T>) {
    return (...args: any[]) => {
         console.log("Called", func.name);
         func(...args);
    };
}

This is better as something like:

function wrap<T, Args extends any[]>(func: (...args: Args) => T) {
    return (...args: Args) => {
         console.log("Called", func.name);
         func(...args);
    };
}

I think the cases where you really don't care what the arguments to a function are is a pretty small niche.

We don't have to remove or deprecate it just because it's niche, but I do think there's a potential source of confusion in having a type that looks more useful than it actually is in practice.

@Retsam
Copy link
Collaborator Author

Retsam commented Mar 12, 2019

Perhaps AnyFunc could be replaced with some more fine-grained function types? More things like Predicate<T>

Looking at java.util.function as inspiration, (When you're looking for nouns, no language has nouns like Java) they've got stuff like:

// Useful
type Supplier<T> = () => T;
type Consumer<T> = (t: T) => void;
type UnaryOperator<T> = (t: T) => T;

// Somewhat less useful
type Func<T, R> = (t: T) => R; // Cound make an arbitrary arity version of this, too
type BiConsumer<T, U> = (t: T, u: U) => void;
type BiPredicate<T, U> = (t: T, u: U) => boolean;
type BiFunc<T, U, R> = (t: T, u: U) => R;

// Everything else seems to be stuff like this, which seems excessive:
type IntConsumer = Consumer<number>

The top three especially seem worth having. (And the Java names are pretty reasonable, IMO, though maybe there are better ones) Supplier<T> would replace a lot of our current usages of AnyFunc<T>.


As for the other major use-case for AnyFunc I've see, wrapping functions, I'm not sure there's any sort of type that can make that easier. I tried a type Wrap<F extends Function> = (f: F) => F, but I can't find a way to make that useful without just needing a ugly cast anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant