Skip to content

Implementing interfaces but being more specific - bug or feature of typescript ? #16735

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

Closed
xnnkmd opened this issue Jun 26, 2017 · 7 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@xnnkmd
Copy link

xnnkmd commented Jun 26, 2017

TypeScript Version: 2.3.4

Unlike java/C#, typescript allows me to implement a function in an interface without having an exact match in the signature. Specificly, I can change the type of function arguments and return types as shown in the example below where I have chosen to be more specific than the interface I am implementing. I think there are benefits of doing so and the example compiles but I am unsure if it is a bug or feature of typescript (I don't want to do things that work by accident but is likely to be caught in a later version of typescript).

Code

With

// Example general interface
export interface AsyncValidator extends Validator {
    validate(c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;
}

// Example implementation of interface where implementation is more specific in argument and return value. 
// Note that AbstractControl is replaced by a subclass & Promise is removed as a return type: 
 public static dynamicPNumberExistsValidator = new class implements AsyncValidator {
        validate(c: FormControl): Observable<ValidationErrors | null>  {
            // code here.
        }
    };;

Expected behavior:

Not sure - should it compile ok as it does now ?

Actual behavior:

Compiles ok.

@DanielRosenwasser DanielRosenwasser added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jun 26, 2017
@DanielRosenwasser
Copy link
Member

This is intentional - function return types should be compared in a covariant manner. Returning a specific type when a more general one is expected is acceptable.

@xnnkmd
Copy link
Author

xnnkmd commented Jun 26, 2017

@DanielRosenwasser Thanks... And is it also ok to change an argument type to be more specific when a general one is expected as the example does ? (FormControl is a subclass of AbstractControl)

@kitsonk
Copy link
Contributor

kitsonk commented Jun 26, 2017

I might be wrong, but it works of "assignability" in the sense that the one type could be assigned to the other, so TypeScript does not raise an exception. Functions in TypeScript often narrow their arguments to only operate on what they are going to operate on, making the code in the function less prone to augmenting things that it shouldn't be aware of. Putting "blinders" on your code horse is an effective pattern in TypeScript and still meets the contract. Functions are also allowed to ignore arguments that are in an interface.

interface SomeFunction {
  (a: string, b: string): void;
}

const foo: SomeFunction = (a) => {
  console.log(a);
}

foo('foo', 'bar');

@xnnkmd
Copy link
Author

xnnkmd commented Jun 27, 2017

Does anyone know any references to dokumentation that specifies exactly how matches between interfaces and implementations should work in detail as discussed here?

@kitsonk
Copy link
Contributor

kitsonk commented Jun 27, 2017

Type Compatibility discusses how TypeScript deals with assignability and soundness.

@xnnkmd
Copy link
Author

xnnkmd commented Jun 28, 2017

@kitsonk Thanks for the very useful link. In particular, I think this quote from your link explains things well:

Function Parameter Bivariance: When comparing the types of function parameters, assignment succeeds if either the source parameter is assignable to the target parameter, or vice versa.

@xnnkmd
Copy link
Author

xnnkmd commented Jun 28, 2017

See also: #14973

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants