Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax input types? #23

Open
achingbrain opened this issue Sep 2, 2021 · 1 comment
Open

Relax input types? #23

achingbrain opened this issue Sep 2, 2021 · 1 comment

Comments

@achingbrain
Copy link

The types for .equalBytes are:

equalBytes(bytes: string | ArrayLike<number>): Assertion;

I was writing a test that ensures marshalling/unmarshalling works as expected. The object being marshalled has an optional field that's a Uint8Array.

// for reasons the type is `{ foo?: Uint8Array }`
const input = createInput('some seemingly unrelated data')

const marshalled = marshal(input)
const output = unmarshal(marshalled)

// now do some assertions

What I'd like to do, and can do without types:

expect(input).to.have.property('foo').that.is.ok
expect(output).to.have.property('foo').that.equalBytes(input.foo)

What I have to do with type checking:

if (!input.foo) {
  throw new Error('input had no `foo`')
}

expect(output).to.have.property('foo').that.equalBytes(input.foo)

The reason being that the type of input.foo is Uint8Array | undefined and undefined is not compatible with string | ArrayLike<number>, so by doing if (!input.foo) and throwing we let tsc know that input.foo is a thing and therefore a Uint8Array and compatible with ArrayLike<number>.

This feels a bit weird because we're using chai to test some properties and tsc to test others, though it's really just a quirk of the type system.

Chai itself specifies values to be tested as any. If this module did the same thing it would allow much more concise tests.

@slowli
Copy link
Owner

slowli commented Sep 4, 2021

Not sure I can see the analogy with Equal interface. The equalBytes argument is not the tested / actual value, it is the expected value. Expected value types are restricted in some Chai typings, e.g. length assertions (Length interface) take a number argument, not any. Making expected values untyped would defeat the purpose of typings, IMO.

In the case you provided, it seems that input.foo is not undefined (otherwise, the expect(input) assertion will fail), but the TypeScript type checker cannot infer that, unlike when the if short circuit is used. (IMO, it's unreasonable to expect the type checker to be this smart, but the root issue is there.) If the test is focused on marshalling, I'd say that running assertions on input is kind of weird, and using ifs instead could help separating test setup from assertions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants