Перейти к содержимому

Система типов

type и файловый $type допускают только: color / dimension / number / string / shadow / duration / cubicBezier (взаимно однозначное соответствие с перечислением в tokens/schema.json; см. TokenType в codegen/src/types.ts).

Допустимые формы значений для каждого типа — в Формат токенов.

Тип листа вычисляется в следующем порядке:

1. Тип, объявленный самим листом
2. Тип цели ссылки ref (только если лист не объявил собственный тип)
3. Файловый / групповой $type
4. Если ничего нет → ошибка на этапе resolve

Этот приоритет реализуется совместными усилиями этапов flatten и resolve.

Этап 1: flatten (проталкивание группового $type вниз)

Заголовок раздела «Этап 1: flatten (проталкивание группового $type вниз)»

flatten из load.ts распространяет $type, объявленный группой (включая корень файла), вниз по дереву, но запекает его только в листья-литералы:

// Токены-литералы наследуют тип группы; ref-токены сохраняют undefined,
// оставляя выбор типа за resolve — он возьмёт его у цели ссылки
if (node.type === undefined && node.ref === undefined && groupType !== undefined) {
out.set(path, { ...node, type: groupType })
}

ref-листья намеренно не «запекаются» — чтобы их не вводил в заблуждение тип по умолчанию того файла, где они находятся.

Этап 2: resolve (тип цели ref перекрывает файловый тип по умолчанию)

Заголовок раздела «Этап 2: resolve (тип цели ref перекрывает файловый тип по умолчанию)»

При сборке светлого отображения в resolve.ts:

token: {
type: raw.type ?? (file.defaultType as ResolvedToken['type']), // сначала файловый $type как запасной вариант
...
},
ownType: raw.type, // нужен, чтобы различать «тип, объявленный листом» и «файловый тип по умолчанию»

После разрешения ref:

// Если собственный тип не объявлен, берётся тип цели ссылки
// (тип токена следует за источником истины)
if (entry.ownType === undefined && resolved.type !== undefined) {
entry.token.type = resolved.type
}

Этот шаг накладывает 2-й уровень (тип цели ref) поверх 3-го (файловый $type). Если тип в итоге остаётся undefined, выбрасывается У токена X нет типа.

Типичный пример: button.fontWeight ссылается на typography.fontWeight.primary:

typography.json
// components.json (файловый $type: dimension)
"button": { "fontWeight": { "ref": "typography.fontWeight.primary" } }
"fontWeight": { "primary": { "value": 400, "type": "number" } }

Если бы ref-лист тоже получал файловый dimension, тип button.fontWeight оказался бы ошибочным. Трёхуровневый приоритет гарантирует, что он возьмёт number у цели ссылки.

Другой пример: в typography.json нет файлового $type — типы задаются групповыми $type (группа fontSize помечена dimension) и собственными type листьев (у fontWeight.primary — number), каждое отвечает за себя.

Схема управляет только структурой. То, соответствует ли тип значению, на этапе resolve проверяет assertTypedValue из format.ts — с фактическим разбором значения:

  • color → должен разбираться функцией parseColor (шестнадцатеричное значение или rgba)
  • dimension → должен соответствовать /^\d+(\.\d+)?(px|%)$/
  • duration → поддерживаются s / ms
  • shadow → разбивается по пробелам; если 4-й сегмент соответствует -?\d+(\.\d+)?px, это spread, иначе — цвет
  • cubicBezier → массив из четырёх чисел

Подробнее: Слой форматирования.

Выведение типа для ключей переопределения тёмной темы проще: raw.type ?? byPath.get(path)?.type. Тёмные ключи без типа не ломают codegen, а попадают в orphanDarkPaths и сообщаются командой pnpm verify:tokens. Подробнее: Слияние тёмного режима.

Copyright © 2024-2026 FengMo WenXue