I have a type predicate:
// tslint:disable-next-line:no-any
const isString = (value: any): value is string {
return typeof value === 'string'
}
This works, but it requires that I disable my linter. I would rather do this:
const isString = <T>(value: T): value is string {
return typeof value === 'string'
}
That way the type is not any
but instead we have 1 typeguard function for each type, that is one function a -> Boolean
for every a
.
Typescript complains that:
A type predicate's type must be assignable to its parameter's type. Type 'string' is not assignable to type 'T'.
This doesn't make sense to me... Why should it matter what the type predicate's type is?
Regarding the error message, the predicate type must be assignable to the value type because the type guard is used to check whether a value with a less-specific type is in fact a value with a more-specific type. For example, consider this guard: function isApe (value: Animal): value is Ape { return /* ...
If there is no relationship between the value's type and the type in the type predicate, the guard would make no sense. For example, TypeScript won't allow a user-defined guard like this: function isString (value: Date): value is string { return typeof value === "string"; } [ts] A type predicate's type must be assignable to its parameter's type.
Type predicates are a special return type that signals to the Typescript compiler what type a particular value is. Type predicates are always attached to a function that takes a single argument and returns a boolean. Type predicates are expressed as argumentName is Type.
[ts] A type predicate's type must be assignable to its parameter's type. Type 'string' is not assignable to type 'Date'. A Date value will never be a string, so the guard is pointless: its runtime check is unnecessary and should always return false.
Adding to the accepted answer, if you happen to need to use a type guard against a mixin, you'll get this error too, since the is
operator doesn't behave as an implements
would.
interface Animal { ... }
interface Climber { ... }
interface Ape extends Animal, Climber { ... }
const isClimberMixin = (animal: Animal): animal is Climber => ...
Such code fails because Climber
is not assignable to Animal
since it doesn't extend from it.
The solution, if you can't avoid the mixin pattern, is to use an union type:
const isClimberMixin = (animal: Animal): animal is Animal & Climber => ...
A user-defined type guard performs a runtime check to determine whether or not value of a particular type satisfies a type predicate.
If there is no relationship between the value's type and the type in the type predicate, the guard would make no sense. For example, TypeScript won't allow a user-defined guard like this:
function isString(value: Date): value is string {
return typeof value === "string";
}
and will effect this error:
[ts] A type predicate's type must be assignable to its parameter's type.
Type 'string' is not assignable to type 'Date'.
A Date
value will never be a string
, so the guard is pointless: its runtime check is unnecessary and should always return false
.
When you specify a generic, user-defined type guard, T
could be anything, so - as with Date
- for some types, the type guard would make no sense.
If you really don't want to use any
, you could use an empty interface - {}
- instead:
function isString(value: {}): value is string {
return typeof value === "string";
}
If you also want to allow for null
and undefined
values to be passed to the guard, you could use:
function isString(value: {} | null | undefined): value is string {
return typeof value === "string";
}
Regarding the error message, the predicate type must be assignable to the value type because the type guard is used to check whether a value with a less-specific type is in fact a value with a more-specific type. For example, consider this guard:
function isApe(value: Animal): value is Ape {
return /* ... */
}
Ape
is assignable to Animal
, but not vice versa.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With