mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-30 07:14:39 +08:00
Read records by keyof keys when their fields differ in type (#1883)
This commit is contained in:
@@ -1321,6 +1321,15 @@ export function lowerRecordKeyRead(
|
||||
shape.fields.every((f) => typeEquals(f.type, shape.fields[0]!.type))
|
||||
) {
|
||||
declared = shape.fields[0]!.type;
|
||||
} else if (shape.fields.length > 0) {
|
||||
// Fields of different types (`row[key]` with `key: keyof Row`): the
|
||||
// checker types the read as the union of the field types the key can
|
||||
// name. When every declared field surfaces in that union (an arm, or a
|
||||
// union that widens into it), the per-field dispatch wraps each hit
|
||||
// into its arm.
|
||||
const read = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
||||
if (read !== null && read.kind === "union" && recordKeyResultOk(lowerer, shape, read))
|
||||
declared = read;
|
||||
}
|
||||
// Fields of different types answer a checked-dynamic value where nothing
|
||||
// typed the read: in JavaScript, and where the checker types it 'any'
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7400-record-keyof-union-read.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Keyed reads of records whose fields have different types, with a key
|
||||
// typed as `keyof` the record: the read is the union of the field types.
|
||||
interface Book {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly year: number;
|
||||
readonly tags: readonly string[];
|
||||
readonly available: boolean;
|
||||
}
|
||||
type BookPatch = Partial<Omit<Book, "id">>;
|
||||
|
||||
function changedKeys(current: Book, patch: BookPatch): string[] {
|
||||
const changed: string[] = [];
|
||||
for (const key of Object.keys(patch) as (keyof BookPatch)[]) {
|
||||
const before = current[key];
|
||||
const after = patch[key];
|
||||
if (after === undefined) continue;
|
||||
if (Array.isArray(before) && Array.isArray(after)) {
|
||||
if (before.join("|") !== after.join("|")) changed.push(key);
|
||||
} else if (before !== after) changed.push(key);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
function describe(book: Book, key: keyof Book): string {
|
||||
const value = book[key];
|
||||
if (typeof value === "string") return `${key}: string ${value}`;
|
||||
if (typeof value === "number") return `${key}: number ${value.toFixed(1)}`;
|
||||
if (typeof value === "boolean") return `${key}: boolean ${value ? "yes" : "no"}`;
|
||||
return `${key}: list of ${value.length} [${value.join(", ")}]`;
|
||||
}
|
||||
|
||||
const book: Book = { id: "b1", title: "Atlas", year: 1999, tags: ["maps", "travel"], available: true };
|
||||
console.log(changedKeys(book, { title: "Atlas", year: 2001 }));
|
||||
console.log(changedKeys(book, { tags: ["maps", "travel"], available: false }));
|
||||
console.log(changedKeys(book, { tags: ["maps"], title: "Atlas II", year: undefined }));
|
||||
for (const key of ["id", "title", "year", "tags", "available"] as const) console.log(describe(book, key));
|
||||
const keys: (keyof Book)[] = ["year", "tags"];
|
||||
const values = keys.map((k) => book[k]);
|
||||
console.log(values, values.length);
|
||||
console.log(JSON.stringify(Object.fromEntries(keys.map((k) => [k, book[k]]))));
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2600, "san": 20500 }
|
||||
Reference in New Issue
Block a user