0

I have the following:

import * as core from '@actions/core'
import { GitHub } from '@actions/github'
import { context } from '@actions/github'

const valid_statuses = ["queued", "in_progress", "completed"] as const;
type Status = typeof valid_statuses[number]

function IsValidJson(str: string): boolean {
    try {
        JSON.parse(str)
    } catch (e) {
        return false
    }
    return true
}

function StringArrayFromJson(json: string): [string] {
    if (json != null && typeof json === 'string' && !IsValidJson(json)) {
        json = `[${ json }]`
    }

    if (json == null) {
        json = '[]'
    }

    if (!IsValidJson(json)) {
        throw new Error(`Invalid JSON: ${ json }`)
    }
    return JSON.parse(json) as [string]
}

async function main(): Promise<void> {
    try {
        let statuses = StringArrayFromJson(core.getInput('statuses', { required: true }))

        //item in valid_statuses returns false!
        if (statuses.length <= 0 || !statuses.every(item => { return item in valid_statuses })) {
            core.setFailed(`ERROR: statuses must be an array of valid status strings. Got: ${ JSON.stringify(statuses) }`)
            return
        }

        console.log('Success')
    } catch (error) {
        core.setFailed(error.message)
    }
}

main()

I realize that code translates to the following Javascript:

const valid_statuses = ["queued", "in_progress", "completed"]
console.log("in_progress" in valid_statuses); //Returns false

How come I can't check if my string is a valid union?

enter image description here

EDIT (TSConfig):

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "esnext",
        "lib": [
            "es2015",
            "es2017"
        ],
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitAny": true,
        "removeComments": false,
        "preserveConstEnums": true
    },
    "include": [
        ".github/actions/**/*.ts",
        "**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

EDIT (package.json):

{
  "private": true,
  "name": "create-check-action",
  "scripts": {
    "build": "tsc",
    "test": "tsc --noEmit && jest"
  },
  "license": "ISC",
  "dependencies": {
    "@actions/core": "^1.2.4",
    "@actions/github": "^2.2.0"
  },
  "devDependencies": {
    "@types/jest": "^25.2.3",
    "@types/node": "^14.0.5",
    "jest": "^26.0.1",
    "ts-jest": "^26.0.0",
    "typescript": "^3.9.3"
  }
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Please add your tsconfig. My guess is that you need to take a newer javascript version – max May 24 '20 at 17:55
  • @zirmax; Added `tsconfig`. – Brandon May 24 '20 at 17:58
  • I cant reproduce your error with the code and config you provided. Is there more code? Please also add your package.json – max May 24 '20 at 18:04
  • I have included ALL the code now and the entire `package.json`. It is a large `github action` so I didn't want to make the post too large originally so I cut it down to where the error was. However, I've now included everything needed to run it. I execute `npm run build` then I push it to a github repo and let the action execute. – Brandon May 24 '20 at 18:16
  • I ended up doing: `function IsValidStatus(status: string): status is Status { return valid_statuses.find(item => { return item === status }) != null }` which seems to work. Just wasn't sure why `includes` and `indexOf` failed. – Brandon May 24 '20 at 18:22
  • Quite possibly a duplicate of [Why does the argument for Array.prototype.includes(searchElement) need the same type as array elements?](https://stackoverflow.com/questions/53033854/why-does-the-argument-for-array-prototype-includessearchelement-need-the-same). There's an awful lot going on in the question though (using `in` on an array is not likely to work, the error in your screenshot is on code you haven't included in the question, pictures of code and error messages are discouraged as mentioned in [ask], etc) so I'm not sure – jcalz May 25 '20 at 01:45
  • @jcalz; The error in the screenshot is 100% in the code I have posted. If you're curious still, I have uploaded the `github action` here: https://github.com/Brandon-T/create-check-action/blob/master/src/index.ts#L74 and I ended up using the solution I mentioned above doing `IsValidStatus`.. because if I attempt to use `string in valid_statuses` or `valid_status.includes` it will give an error. In the github link, I used `.find` on line 11 to work around `.includes` and `.indexOf` giving the errors mentioned in the screenshot. – Brandon May 25 '20 at 01:55
  • 1
    Using `in` won't work because it checks keys, not values (`"a" in {a: 1}` is `true`, but `"a" in ["a"]` is `false`). The issue with `includes` and `indexOf` is a TypeScript typing issue, and the answer to that is in the linked duplicate question. The simplest way to deal with that is something like `(valid_statuses as readonly string[]).indexOf(item)` or `(valid_statuses as readonly string[]).includes(item)`. You can use `find()` if you want, but that's a workaround and doesn't seem to address the question as stated. Do you still want an answer or do you want to delete the question? – jcalz May 25 '20 at 02:36

1 Answers1

0

Already tried with includes?

const valid_statuses = ["queued", "in_progress", "completed"]

console.log(valid_statuses.includes("in_progress"));
console.log(valid_statuses.includes("queued"));
console.log(valid_statuses.includes("something_else"));
Balastrong
  • 4,336
  • 2
  • 12
  • 31
  • I added an image to the OP that shows the error I get when I try `includes`. – Brandon May 24 '20 at 17:47
  • I'm not sure why you add `as const` at the end of `const valid_statuses = ["queued", "in_progress", "completed"] as const;` What happens if you remove it? – Balastrong May 24 '20 at 18:22
  • https://stackoverflow.com/questions/36836011/checking-validity-of-string-literal-union-type-at-runtime/61129291#61129291 that's where I got it from. If I don't add the `const` at the end, it will give a compiler error that `string` cannot be converted to `type Status` when doing `str as Status`. – Brandon May 24 '20 at 18:23