Issue
I have a model with Activiy Interface and Mongo Schema, and a function that takes an ID and logs it. Then in my route I'm querying all activities from a given customer where the externalId is not undefined. With the returned array I call that function defined in my model, passing externalId as param.
But TypeScript keeps throwing: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. Type 'undefined' is not assignable to type 'number'.
I already tried to wrap the call with an if statement checking if externalId && typeof externalId === 'number'
, but I can't make the error go away.
Model:
import { Schema, model, } from 'mongoose';
interface IActivity {
customer: string,
externalId?: number,
}
const activitySchema: Schema = new Schema({
Customer: {
type: String,
required: true,
minlength: 5,
maxlength: 50,
},
externalId: { type: Number, },
});
export function updateExternal(externalId: number): void {
console.log(externalId);
}
export const Activity = model<IActivityModel>('Activity', activitySchema);
Route:
const completeActivity = await Activity
.find({
'externalId': { '$ne': undefined, },
'customer': 'Foo Customer',
});
completeActivity.forEach((activity) => {
updateExternal(activity.externalId); // This is where the error appears
});
Solution
It's interesting to think what should happen in order to get the value at a key, when the key may not really have a value. activity.externalId
has type number | undefined
so what shall we do to move from number | undefined
to number
?
We need to 'remove' that part of the type, that is to say, we need to provide some way to convert all the undefined
parts of number | undefined
to number
.
Often we do this by providing some default - we say that whenever a value is undefined
, don't use it - use the default instead. It is difficult to get a good type for this with typescript, though; we can't specify that the type of the object has the property we're looking for; so we must simply say that it can be any
, and as such, the function would only be able to return any
, as in:
interface IX {
a?: number;
}
function propOr<K extends string | number, U, O extends Record<K, U>>(k: K, defaultValue: U, obj: O) {
return obj[k];
}
const testFunction1 = (x: IX) => x.a; // has type number | undefined
const testFunction2 = (x: IX) => propOr('a', -1, x); // type error:
The type error we get here is:
Argument of type 'IX' is not assignable to parameter of type 'Record<"a", number>'. Types of property 'a' are incompatible. Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.(2345)
We might look at other libraries, to see what they do, Sanctuary, for instance, has it as:
prop(p: string): (q: any) => any;
So when typing a function, we're a little stuck, we're forced to lose the type information (unless someone else can jump in with a better idea).
The best solution I can think of, is to just use an or to always get a number:
interface IX {
a?: number;
}
const a: IX = {};
const b = a.a || 0;
Now b
has the type number
.
Answered By - OliverRadini
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.