Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions http/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ function validateDomain(domain: string) {
/**
* Parse cookies of a header
*
* The returned object has a null prototype (so it does not inherit from
* `Object.prototype` — properties like `toString` or `hasOwnProperty` will be
* `undefined` rather than the inherited methods). The return type is
* `Partial<Record<string, string>>` to reflect that arbitrary key access may
* yield `undefined` when no cookie of that name was set.
*
* @example Usage
* ```ts
* import { getCookies } from "@std/http/cookie";
Expand All @@ -242,15 +248,21 @@ function validateDomain(domain: string) {
* headers.set("Cookie", "full=of; tasty=chocolate");
*
* const cookies = getCookies(headers);
* assertEquals(cookies, { full: "of", tasty: "chocolate" });
* assertEquals(cookies.full, "of");
* assertEquals(cookies.tasty, "chocolate");
* assertEquals(cookies.missing, undefined);
* ```
*
* @param headers The headers instance to get cookies from
* @return Object with cookie names as keys
* @return Object with cookie names as keys (null-prototype). Values are
* typed as `string | undefined` so missing-key access is caught at compile
* time.
*/
export function getCookies(headers: Headers): Record<string, string> {
export function getCookies(
headers: Headers,
): Partial<Record<string, string>> {
const cookie = headers.get("Cookie");
const out: Record<string, string> = Object.create(null);
const out: Partial<Record<string, string>> = Object.create(null);
if (cookie !== null) {
const c = cookie.split(";");
for (const kv of c) {
Expand Down
9 changes: 6 additions & 3 deletions http/cookie_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ Deno.test({
fn() {
const headers = new Headers([["Cookie", "foo=bar"]]);
const cookies = getCookies(headers);
assertType<IsExact<typeof cookies, Record<string, string>>>(true);
// allowed due to `noUncheckedIndexedAccess`
const baz = cookies.baz as undefined;
// Return type is Partial<Record<string, string>> so arbitrary key access
// surfaces as `string | undefined` regardless of `noUncheckedIndexedAccess`.
// Closes the type/runtime gap reported in #6852 and #7053 — at runtime the
// returned object is null-prototyped, so missing keys really are undefined.
assertType<IsExact<typeof cookies, Partial<Record<string, string>>>>(true);
const baz: string | undefined = cookies.baz;
assertEquals(baz, undefined);
assertEquals(Object.getPrototypeOf(cookies), null);
assertEquals(cookies.toString, undefined);
Expand Down
Loading