#Is there a way to get the compiler tor recognise the values in the object can't be undefined?

35 messages · Page 1 of 1 (latest)

gritty vortex
#

If aggregatedRules has a key, its value will be an array of strings always.

function aggregateLabelsByRule (rules: LabelingRule[]): { [key in LabelingAction]?: string[] } {
  const aggregatedRules: { [key in LabelingAction]?: string[] } = {}

  for(const rule of rules) {
    if(rule.action in aggregatedRules) {
      aggregatedRules[rule.action].push(...rule.labels)
    } else {
      aggregatedRules[rule.action] = rule.labels
    }
  }

  return aggregatedRules
}
src/validateConfig.ts:12:7 - error TS2532: Object is possibly 'undefined'.

12       aggregatedRules[rule.action].push(...rule.labels)
burnt edge
#

Easiest way is to rewrite it into:

for (const rule of rules) {
    (aggregatedRules[rule.action] ??= []).push(...rule.labels)
}
polar dirge
#

Or:

const maybeArray = aggregatedRules[rule.action];
if(maybeArray) {
  maybeArray.push(...rule.labels)
} else {
  aggregatedRules[rule.action] = rule.labels
}
gritty vortex
#

The compiler still thinks the object returned has values with type string[] | undefined. Is there a way around this?

burnt edge
#

I mean, nothing in your code says that rules will contain all LabelingActions.

pure apex
#

Would adding an exclamation point on line 12 like below work?

function aggregateLabelsByRule (rules: LabelingRule[]): { [key in LabelingAction]?: string[] } {
  const aggregatedRules: { [key in LabelingAction]?: string[] } = {}

  for(const rule of rules) {
    if(rule.action in aggregatedRules) {
      aggregatedRules[rule.action]!.push(...rule.labels)
    } else {
      aggregatedRules[rule.action] = rule.labels
    }
  }

  return aggregatedRules
}
pure apex
#

Let me know if it was what you were looking for @gritty vortex 😇

gritty vortex
#

huh I guess just following doesn't give me notifications of any kind

#

@pure apex No didn't work. The return type of the function is still an object with values string[] | undefined

#

Oh wait I think I'm misunderstanding how I should use the return value

#

No wait I was just confused. Sorry it's late rn.

#

Ok so I have a problem after I call aggregateLabelsByRule

#

It is called like this

function determineLabelingRules (rules: LabelingRule[]): LabelingRule[] {
//...irrelevant code
    const aggregatedLabels = aggregateLabelsByRule(rules)
    const aggregatedLabelRules: LabelingRule[] = []
    const addLabels = aggregatedLabels[LabelingAction.ADD]
    const removeLabels = aggregatedLabels[LabelingAction.REMOVE]

    if (addLabels && addLabels.length) {
      aggregatedLabelRules.push({
        action: LabelingAction.ADD,
        labels: addLabels
      })
    }

    if (removeLabels && removeLabels.length) {
      aggregatedLabelRules.push({
        action: LabelingAction.REMOVE,
        labels: removeLabels
      })
    }

    return aggregatedLabelRules
}

I want to just

    const aggregatedLabels = aggregateLabelsByRule(rules)
    const aggregatedLabelRules: LabelingRule[] = []
    for (const action in aggregatedLabels) {
      aggregatedLabelRules.push({
        action: action as LabelingAction,
        labels: aggregatedLabels[action as LabelingAction]
      })
    }
return aggregateLabelsByRule(rules)```
 but I can't because the labels of a `LabelingRule` can't be `undefined` so I have to add some unnecessary checks before returning a value from `aggregateLabelsByRule`.
#

LabelingRule and LabelingAction are

enum LabelingAction {
  ADD = "ADD",
  REMOVE = "REMOVE",
  SET = "SET"
}

interface LabelingRule {
  action: LabelingAction
  labels: string[]
}
gritty vortex
#

Made an edit to the second post before this one now

burnt edge
#

But really, does aggregateLabelsByRule need to return an object? Can't it just return an array of key value pairs?

#

Or a Map could also work.

gritty vortex
#

oh yeah. I can just convert it back to LabelingRule[] before I return. I'll do that

#

wait if I convert it back in aggregateLabelsByRule I think I still have to check if the values are undefined which I know they're not

#

Is there no way to express
the keys of the object must be values from a string enum and if the key is present in the object then its value must be defined?

burnt edge
#

Once again, there's nothing in you code that says rules must contain all rules.

#

What if someone just calls your function with determineLabelingRules([])? Clearly that array doesn't have add or remove, so TS is correct that you need to check and make sure add and remove exist before you can use them.

#

But I also don't understand what your code is supposed to do:

    const aggregatedLabels = aggregateLabelsByRule(rules)
    const aggregatedLabelRules: LabelingRule[] = []
    for (const action in aggregatedLabels) {
      aggregatedLabelRules.push({
        action: action as LabelingAction,
        labels: aggregatedLabels[action as LabelingAction]
      })
    }
return aggregateLabelsByRule(rules)

Why are you aggregating it, then unpacking the result, then aggregating the result again?

gritty vortex
#

I meant

const aggregatedLabels = aggregateLabelsByRule(rules)
    const aggregatedLabelRules: LabelingRule[] = []
    for (const action in aggregatedLabels) {
      aggregatedLabelRules.push({
        action: action as LabelingAction,
        labels: aggregatedLabels[action as LabelingAction]
      })
    }
return aggregatedLabelRules
gritty vortex
burnt edge
#

Ah, in that case it's because of this

#

!:unsafe-keys

inner turtleBOT
#
retsam19#0
`!retsam19:unsafe-keys`:

Since TS allows objects to have extra properties not specified in the type, it doesn't assume that all the keys on the type are the only keys on the object. This means that Object.keys returns string[] not a specific type, and for(const key in obj), key is string, (not keyof typeof obj).

If you wish to assume otherwise, this utility is often helpful:

// A signature for `Object.keys` that assumes the only keys are the ones indicated by the type
const unsafeKeys = Object.keys as <T>(obj: T) => Array<keyof T>;
burnt edge
#

@gritty vortex See above.

#

But yeah I would suggest just making aggregateLabelsByRule return key value pairs or a Map, rather than an object.

#

That will get rid of the problem.

gritty vortex
#

Ok sounds good. Thanks

gritty vortex
#
  Type 'undefined' is not assignable to type 'string[]'.

38         labels: aggregatedLabels.get(action as LabelingAction)

nope

#

Oh no wait yes. I just didn't update how to iterate through the map