添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am new to ts and have just started migrating my app from js to ts. first function - calcAvg, takes an array of numbers and returns their average. second function - takes a list of objects. each object has daily statistics (see DailyData interface). TS marks calcAvg(marginArray) as an error - "Argument of type '(number | null)[]' is not assignable to parameter of type 'number[]'. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.ts(2345) const marginRevArray: (number | null)[]"

Before calcAvg , I am checking the array for nulls and if there is one or more nulls values I don't calculate the average.

Any ideas what am I missing here? Thanks!

*** see code below

interface DailyData {
  id: number
  cost: number
  margin: number
const calcAvg = (dataList: number[]) => {
  const reducer = (accumulator: number, curr: number) => accumulator + curr;
  const dataSum = dataList.reduce(reducer, 0);
  return dataSum / dataList.length;
const avgMargin = (dailyData: DailyData[]) => {
  const marginArray = dailyData.map((obj: DailyData) => obj.cost > 0 ? obj.margin/obj.cost: null);
  if (marginArray.some((el: number|null) => el=== null)) {
    //if the array has null values don't calculate
    return 'no data';
  return calcAvg(marginArray);
                Please provide a minimal reproducible example that clearly demonstrates the issue you are facing.  Ideally someone could paste the code into a standalone IDE like The TypeScript Playground (link here!) and immediately get to work solving the problem without first needing to re-create it.  So there should be no pseudocode, typos, unrelated errors, or undeclared types or values.
– jcalz
                Jan 20, 2022 at 20:19
                You'll have to use type inferrence, typescript is just not smart enough to understand that marginArray doesn't contain any nulls. So the last line should be return calcAvg(marginArray as number[]);
– ShamPooSham
                Jan 20, 2022 at 20:22
                If you can refactor to use every instead of some, then you can annotate to use it as a type guard like this.  Do you want me to write up an answer? Let me know. Either way, you should fix the typos in your question code.
– jcalz
                Jan 20, 2022 at 20:28
                @Bergi Thanks for the correction, you're right that I didn't use the correct term (I brainfarted). But the correct term is actually  "assertion". Casting implies that the data is changed in runtime (or at least some information about how the runtime should interpret the data). This doesn't happen in typescript since the javascript isn't changed.
– ShamPooSham
                Jan 20, 2022 at 21:07

The TypeScript standard library call signature for Array's some() method looks like

interface Array<T> {
  some(
    predicate: (value: T, index: number, array: T[]) => unknown, 
    thisArg?: any
  ): boolean;

which means that when you call marginArray.some(el => el === null), all the compiler knows is that this will return a boolean. It has no idea that this should have any implication whatsoever for the type of marginArray. So the type of marginArray is still (number | null)[] if the call returns true, and so the compiler balks at

calcAvg(marginArray) // error! marginArray might have nulls in it, I think

If you want to get rid of the error without refactoring, you can use a type assertion. Type assertions are meant for situations like this where you, the developer, know more about the type a value will have than the compiler can figure out. If you are sure that marginArray will be a number[] by the time you call calcAvg(), then you can just tell the compiler that:

calcAvg(marginArray as number[]); // okay

This works, but is not a magic cure-all; by using a type assertion you are taking the responsibility onto yourself for its accuracy. The compiler doesn't know if marginArray has nulls in it or not; it just believes you when you tell it. If you accidentally lie to the compiler, it won't catch it:

if (marginArray.some(el => el !== null)) { // 😅 wrong check
  return 'no data';
return calcAvg(marginArray as number[]); // still no error

So you should double and triple check whenever you make a type assertion that you're doing it right.

On the other hand, if you don't mind some mild refactoring, you can switch from some() to Array's every() method, whose standard library typings includes the following call signature:

interface Array<T> {
  every<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S, 
    thisArg?: any
  ): this is S[];

This says that if you pass a user-defined type guard function in as predicate which can narrow the type of the array elements, then every() can itself be used as a user-defined type guard function which can narrow the type of the whole array. So if predicate is annotated so that a true return value implies that value is number and not null, then a true return value from every() will tell the compiler that the whole array is a number[]:

if (!marginArray.every((el: number | null): el is number => el !== null)) {
  return 'no data';
return calcAvg(marginArray); // okay

See how the callback function has been annotated to be of type (el: number | null) => el is number? Now the compiler understands that if calcAvg() is reached, then marginArray is a number[].

This is probably the closest you can get to type safety. I say "closest" because the act of annotating a function as a user-defined type guard is still the developer telling the compiler something it can't figure out. It would be nice if el => el !== null would automatically be inferred as a type guard, but it's not. There's at least one open feature request about this: see microsoft/TypeScript#38390. So you can still make the same kind of mistake as above and the compiler won't catch it:

if (!marginArray.every((el: number | null): el is number => el === null)) { // 😅
  return 'no data';

At least in the refactored version, though, the scope of potential mistakes is narrower. You have to write el is number right next to where you write el !== null, whereas the original version has you writing marinArray as number[] farther away from where the relevant check is taking place.

Playground link to code

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.