Skip to content

Commit 8af2407

Browse files
author
Brian Chen
committed
add more tests and fix PartialWithFieldValue
1 parent 1450b76 commit 8af2407

File tree

2 files changed

+94
-9
lines changed

2 files changed

+94
-9
lines changed

packages/firestore/src/lite-api/reference.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@ export interface DocumentData {
5454
* Similar to Typescript's `Partial<T>`, but allows nested fields to be
5555
* omitted and FieldValues to be passed in as property values.
5656
*/
57-
export type PartialWithFieldValue<T> = Partial<WithFieldValue<T>>;
57+
export type PartialWithFieldValue<T> =
58+
| Partial<T>
59+
| (T extends Primitive
60+
? T
61+
: T extends {}
62+
? { [K in keyof T]?: PartialWithFieldValue<T[K]> | FieldValue }
63+
: never);
5864

5965
/**
6066
* Allows FieldValues to be passed in as a property value while maintaining
@@ -66,7 +72,8 @@ export type WithFieldValue<T> =
6672
? T
6773
: T extends {}
6874
? { [K in keyof T]: WithFieldValue<T[K]> | FieldValue }
69-
: Partial<T>);
75+
: never);
76+
7077
/**
7178
* Update data (for use with {@link (updateDoc:1)}) that consists of field paths
7279
* (e.g. 'foo' or 'foo.baz') mapped to values. Fields that contain dots

packages/firestore/test/lite/integration.test.ts

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,44 @@ describe('withConverter() support', () => {
13991399
);
14001400
});
14011401
});
1402+
1403+
it('allows omitting fields', async () => {
1404+
return withTestDoc(async doc => {
1405+
const ref = doc.withConverter(testConverterMerge);
1406+
1407+
// Omit outer fields
1408+
await setDoc(
1409+
ref,
1410+
{
1411+
outerString: deleteField(),
1412+
nested: {
1413+
innerNested: {
1414+
innerNestedNum: increment(1)
1415+
},
1416+
innerArr: arrayUnion(2),
1417+
timestamp: serverTimestamp()
1418+
}
1419+
},
1420+
{ merge: true }
1421+
);
1422+
1423+
// Omit inner fields
1424+
await setDoc(
1425+
ref,
1426+
{
1427+
outerString: deleteField(),
1428+
outerArr: [],
1429+
nested: {
1430+
innerNested: {
1431+
innerNestedNum: increment(1)
1432+
},
1433+
timestamp: serverTimestamp()
1434+
}
1435+
},
1436+
{ merge: true }
1437+
);
1438+
});
1439+
});
14021440
});
14031441

14041442
describe('WithFieldValue', () => {
@@ -1421,7 +1459,7 @@ describe('withConverter() support', () => {
14211459
});
14221460
});
14231461

1424-
it('requires all fields to be present', async () => {
1462+
it('requires all outer fields to be present', async () => {
14251463
return withTestDoc(async doc => {
14261464
const ref = doc.withConverter(testConverter);
14271465

@@ -1440,6 +1478,24 @@ describe('withConverter() support', () => {
14401478
});
14411479
});
14421480

1481+
it('requires all nested fields to be present', async () => {
1482+
return withTestDoc(async doc => {
1483+
const ref = doc.withConverter(testConverter);
1484+
1485+
await setDoc(ref, {
1486+
outerString: 'foo',
1487+
outerArr: [],
1488+
// @ts-expect-error
1489+
nested: {
1490+
innerNested: {
1491+
innerNestedNum: increment(1)
1492+
},
1493+
timestamp: serverTimestamp()
1494+
}
1495+
});
1496+
});
1497+
});
1498+
14431499
it('validates inner and outer fields', async () => {
14441500
return withTestDoc(async doc => {
14451501
const ref = doc.withConverter(testConverter);
@@ -1497,16 +1553,38 @@ describe('withConverter() support', () => {
14971553
});
14981554
});
14991555

1556+
it('allows certain types but not others (prevent breaking changes)', () => {
1557+
const withTryCatch = async (fn: () => Promise<void>): Promise<void> => {
1558+
try {
1559+
await fn();
1560+
} catch {}
1561+
};
1562+
1563+
return withTestDoc(async doc => {
1564+
// @ts-expect-error
1565+
await withTryCatch(() => setDoc(doc, 1));
1566+
// @ts-expect-error
1567+
await withTryCatch(() => setDoc(doc, 'foo'));
1568+
// @ts-expect-error
1569+
await withTryCatch(() => setDoc(doc, false));
1570+
await withTryCatch(() => setDoc(doc, undefined));
1571+
await withTryCatch(() => setDoc(doc, null));
1572+
await withTryCatch(() => setDoc(doc, [0]));
1573+
await withTryCatch(() => setDoc(doc, new Set<string>()));
1574+
await withTryCatch(() => setDoc(doc, new Map<string, number>()));
1575+
});
1576+
});
1577+
15001578
describe('used as a type', () => {
15011579
class ObjectWrapper<T> {
1502-
withFieldValueT(value: WithFieldValue<T>): void {
1503-
// eslint-disable-next-line no-console
1504-
console.log(value);
1580+
withFieldValueT(value: WithFieldValue<T>): WithFieldValue<T> {
1581+
return value;
15051582
}
15061583

1507-
withPartialFieldValueT(value: PartialWithFieldValue<T>): void {
1508-
// eslint-disable-next-line no-console
1509-
console.log(value);
1584+
withPartialFieldValueT(
1585+
value: PartialWithFieldValue<T>
1586+
): PartialWithFieldValue<T> {
1587+
return value;
15101588
}
15111589

15121590
// Wrapper to avoid having Firebase types in non-Firebase code.

0 commit comments

Comments
 (0)