Typescript: [bug] TemplateStringsArray is incompatible with literal array type

Created on 15 Jun 2017  ·  7Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 2.4.0

Code

// A *self-contained* demonstration of the problem follows...
interface SQLQuery<TResult> {
  __result: TResult;
} 
declare function sql(
  literals: ['SELECT id FROM users'],
  ...placeholders: any[]
): SQLQuery<{id: number}>;

declare function querySync<TResult>(q: SQLQuery<TResult>): Array<TResult>;

const values: Array<{id: number}> = querySync(sql`SELECT id FROM users`);

Expected behavior:

Typescript should see that the input string 'SELECT id FROM users' matches the expected literals of ['SELECT id FROM users'] and use the declared function, allowing me to generate an overloaded version of the sql function for each query.

Actual behavior:

I get the error:

src/index.ts(23,50): error TS2345: Argument of type 'TemplateStringsArray' is not assignable to parameter of type '["SELECT id FROM users"]'.
Property '0' is missing in type 'TemplateStringsArray'.

In Discussion Suggestion

All 7 comments

I think this is contingent on #16592.

Actually, I don't know if this will work even with the changes I have in mind. TemplateStringsArray is effectively a ReadonlyArray<string> - but you can't assign a ReadonlyArray to a mutable Array. So at the minimum you'd have to write something like

interface MyCustomType extends TemplateStringsArray {
  0: "SELECT id FROM users"
}

That would be fine. I'm going to be auto-generating these definitions anyway, by parsing the code to see which functions are called. This would be very much a workaround for #16551.

At the moment, that doesn't seem to work either though.

declare function id<T extends string>(list: Array<T>): Array<T>;
declare function readonlyId<T extends string>(list: ReadonlyArray<T>): ReadonlyArray<T>;

type LiteralType = "foo" | "bar";

const array: Array<LiteralType> = [];
const readonlyArray: ReadonlyArray<LiteralType> = [];

const foo: ReadonlyArray<LiteralType> = id(array);
const bar: ReadonlyArray<LiteralType> = readonlyId(array);
//    ~~~
// Type 'ReadonlyArray<string>' is not assignable to type 'ReadonlyArray<LiteralType>'.
//  Type 'string' is not assignable to type 'LiteralType'.const bar: ReadonlyArray<LiteralType>
const baz: ReadonlyArray<LiteralType> = readonlyId(readonlyArray);

Isn't it a bug that generic type information is being lost when ReadonlyArray is assigned to Array?

@r00ger that looks like a separate bug, it should probably have its own issue.

@DanielRosenwasser can you give an update on next steps here?

There shouldn't be a type TemplateStringsArray, it should be a const string tuple type, so we can write code:

interface SQL<TSA, VS> {
  texts: TSA;
  values: VS;
}

function sql<TSA extends readonly string[], VS extends any[]>(texts:TSA, ...values: VS): SQL<TSA, VS> {
  return { texts, values };
}

// then
let s: SQL<['select * from person where a=', ' and b=', ''], [number, Date]> = sql`select * from person where a=${1} and b=${new Date()}`;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

wmaurer picture wmaurer  ·  3Comments

kyasbal-1994 picture kyasbal-1994  ·  3Comments

weswigham picture weswigham  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments