ClickHouse / clickhouse-js

Official JS client for ClickHouse DB
https://clickhouse.com
Apache License 2.0
205 stars 25 forks source link

Type bug with insert #199

Closed paymog closed 1 year ago

paymog commented 1 year ago

Describe the bug

I would expect the following code to have type issues

  import { createClient } from "@clickhouse/client";

  const clickhouseClient = createClient({
    host: CLICKHOUSE_URL,
    username: CLICKHOUSE_USER,
    password: CLICKHOUSE_PASSWORD,
    application: "my-application",
    database: CLICKHOUSE_DATABASE,
  });

  await clickhouseClient.insert<{ a: number }>({
    table: CLICKHOUSE_SUBGRAPH_STATUS_TABLE,
    format: "JSONEachRow",
    values: statuses.map((status) => ({
      db_name: status.dbName,
      ipfs_hash: status.ipfsHash,
      synced: status.synced,
      failed: status.failed,
    })),
  });

and when I hover of values in my IDE I’m told it has a type of unknown

image

Expected behaviour

I expect the generic of the insert method to be applied to the shape of the args for the values property

Configuration

Environment

slvrtrn commented 1 year ago

Can you please provide the entire code snippet, including imports?

paymog commented 1 year ago

Can you please provide the entire code snippet, including imports?

I just updated the code snippet with more context. If you need more information please let me know.

EDIT: looks like there's probably something wrong with my setup. I just tried this in replit and tsc is in fact catching the typing issue.

image
slvrtrn commented 1 year ago

Yep, it does not reproduce on my machine as well. For example:

image

WebStorm 2023.1.2 TS 4.9.5 tsconfig.json

paymog commented 1 year ago

I think I got it. I was trying to simplify the code in the example. Here's a much fuller code sample which had the issue:

async function saveStatusesToDb(
  statuses: SubgraphStatus[],
  clickhouseClient: ClickHouseClient,
  logger: Logger
) {
  // stuff
  await clickhouseClient.insert<{ a: number }>({
    table: CLICKHOUSE_SUBGRAPH_STATUS_TABLE,
    format: "JSONEachRow",
    values: statuses.map((status) => ({
      db_name: status.dbName,
      ipfs_hash: status.ipfsHash,
      synced: status.synced,
      failed: status.failed,
    })),
  });
}

When I hovered over the clickhouseClient I saw that it had a generic of unknown

image

I checked the repl and saw that the client had a generic of Readable

image

When I update my function parameter declaration like so (with a <Readable> generic)

async function saveStatusesToDb(
  statuses: SubgraphStatus[],
  clickhouseClient: ClickHouseClient<Readable>,
  logger: Logger

The types start getting inferred correctly

image
paymog commented 1 year ago

So tldr: when accepting a client as a function parameter, it must be typed with a <Readable> generic for the type system to work properly. Not sure why that's the case.

Here's an example which demonstrates the issue (at least in my repl):

import { ClickHouseClient } from "@clickhouse/client";

function test(c: ClickHouseClient) {
  c.insert<{ a: number }>({
  table: "my-table",
  values: { c: "b" }
})
}
slvrtrn commented 1 year ago

Yep, ClickHouseClient is now (after 0.2.0) a generic type with different stream types depending on the implementation.