<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tony Goodchild</title>
    <description>The latest articles on DEV Community by Tony Goodchild (@tonygoodchild).</description>
    <link>https://dev.to/tonygoodchild</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4138285%2F0d9d3906-b917-46ec-88f0-2caa58084795.jpg</url>
      <title>DEV Community: Tony Goodchild</title>
      <link>https://dev.to/tonygoodchild</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tonygoodchild"/>
    <language>en</language>
    <item>
      <title>We built a data grid for technical data. Here is what a column type has to know.</title>
      <dc:creator>Tony Goodchild</dc:creator>
      <pubDate>Thu, 24 Sep 2026 12:27:32 +0000</pubDate>
      <link>https://dev.to/tonygoodchild/we-built-a-data-grid-for-technical-data-here-is-what-a-column-type-has-to-know-8co</link>
      <guid>https://dev.to/tonygoodchild/we-built-a-data-grid-for-technical-data-here-is-what-a-column-type-has-to-know-8co</guid>
      <description>&lt;p&gt;Most data grids know at least four kinds of value: text, number, date, boolean. That is fine for an order list. It falls apart the moment the data is a link budget, a pressure log, a register dump or a fleet of IP addresses, because every one of those has rules that a plain number does not know. &lt;/p&gt;

&lt;p&gt;A gigabyte column that shows 0.5 is wrong. A temperature column with a footer total is wrong. An average bearing of 359° and 1° that reads 180° is wrong in a way that looks right. You can format the output easily, but then you have to start building custom comparators for sorting, and have to enforce how data is entered.&lt;/p&gt;

&lt;p&gt;I built Lattice Grid for applications (like our others) that show real amounts of technical data, so the type system had to carry that knowledge instead of leaving it to every screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A type is a bundle of behaviour, not a label&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Lattice Grid, setting type on a column configures everything at once: how a value is formatted, how typed text is parsed back into a value, how two values compare when sorting, how the column is stored in the columnar backing, what lands in Excel, and what goes on the clipboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;       &lt;span class="c1"&gt;// 1500     → "1.5 km"&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inlet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pressure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;     &lt;span class="c1"&gt;// 200000   → "200 kPa"&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;capacitance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// 4.7e-11  → "47 pF"&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bearing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;degrees&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inletTemp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;celsius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gateway&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ipv4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex32&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have 7 core types (text, number, boolean, date, dateString, object, lookup) and 88 technical ones ship, grouped by domain:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temporal&lt;/strong&gt;: datetime, timestamp, time, duration&lt;br&gt;
&lt;strong&gt;Network&lt;/strong&gt;: ipv4, ipv6, cidr, mac&lt;br&gt;
&lt;strong&gt;Numeric bases&lt;/strong&gt;: hex, hex8, hex16, hex32, binary, binary8, octal&lt;br&gt;
&lt;strong&gt;Computing units&lt;/strong&gt;: bytes, megabytes, gigabytes, bitrate, gigabits&lt;br&gt;
&lt;strong&gt;Physical and engineering units&lt;/strong&gt;: length, mass, duration, speed, acceleration, area, volume, energy, power, force, pressure, torque, density, flow and angle&lt;br&gt;
&lt;strong&gt;Electrical and scientific&lt;/strong&gt;: voltage, current, resistance, capacitance, inductance, charge, conductance, flux density, illuminance, absorbed and equivalent dose, radioactivity, molarity, viscosity, thermal conductivity, frequency and more&lt;br&gt;
&lt;strong&gt;Temperature&lt;/strong&gt;: celsius, fahrenheit, kelvin&lt;br&gt;
&lt;strong&gt;Currency&lt;/strong&gt;: currency, usd, eur, gbp, jpy&lt;br&gt;
&lt;strong&gt;Structured&lt;/strong&gt;: json, colour, rating, percent&lt;br&gt;
&lt;strong&gt;Units&lt;/strong&gt;: store one number, make only the display unit-aware&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Units: store one number, make only the display unit-aware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design rule for every unit type is the same. The column stores a plain number in a named base unit, and only display and input know about units. A gigabyte column holding 0.5 shows 512 MB, accepts 512M typed over it, and stores 0.5 throughout. Because the backing is still a typed array, sorting, filtering, grouping and totals are ordinary arithmetic and never touch rendered text.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;display: 'auto'&lt;/code&gt; walks the coherent SI ladder and picks the largest unit that fits. significantFigures rounds to a fixed precision rather than a fixed number of decimals, and rounding happens before the unit is chosen, so 999,999 bytes to three figures is 1.00 MB, not 1,000 kB. compound: ['ft', 'in'] renders one stored number as 5 ft 11 in and parses it back the same way.&lt;/p&gt;

&lt;p&gt;Seventeen unit systems ship, and you can add your own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerUnitSystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineUnit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createUnitType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@toclocoinc/lattice-grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// factor is how many BASE units one of these is. Exactly one must be 1.&lt;/span&gt;
&lt;span class="nf"&gt;registerUnitSystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yarn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;defineUnit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="nf"&gt;defineUnit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ktex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;defineUnit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;den&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;denier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nf"&gt;createGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;dataTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;linearDensity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createUnitType&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yarn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;linearDensity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The rules that protect the data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three decisions in the unit layer are there because the alternative is silent corruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ambiguous units are refused, not guessed&lt;/strong&gt;. A US gallon and an imperial gallon differ by about a fifth, and "ton" means three different masses. Each has its own symbol (gal (US), ton (UK)), and the bare word is rejected on input rather than resolved to a default. The same rule catches case: mV and MV are a billion apart, so both exact spellings work and mv is refused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customary units are accepted but never chosen&lt;/strong&gt;. With the calorie, the BTU and the kilojoule all on one ladder, auto would render 4,000 J as 3.79 BTU because the BTU happens to be the larger unit. So auto walks SI only; ask for a BTU by name and you get one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some quantities are not units at all&lt;/strong&gt;. Temperature carries an offset, so no factor converts it: celsius, fahrenheit and kelvin convert on input (type 72 F into a Celsius column and it stores 22.2) and refuse to be summed, because twenty degrees plus twenty degrees is not forty. Angles wrap, so a degrees column averages by direction and reports nothing when the angles cancel, while the sum stays arithmetic because 720° of total rotation is a real figure. Currency is an amount and a code, never a fixed factor; you pass a rate source to createCurrencyType, and a rate that is needed but absent renders as a loud missingRate marker, never as zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time: a wall clock and an instant are different types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A date column stores '2024-03-11' as a string, not a Date. new Date('2024-03-11') is midnight UTC, which renders as the 10th in New York, so a delivery date set in London would show a day early in Mumbai. Storing the wall-clock string means the date a user typed is the date everyone sees. It is also faster: ISO 8601 sorts lexicographically in chronological order, and repeated dates dictionary-encode well.&lt;/p&gt;

&lt;p&gt;When you mean a genuine moment, an audit time or a cross-region log line, use timestamp. It ingests epoch-millis, a Date or a zone-bearing ISO string, stores epoch-millis UTC, and sorts, filters and compares on the instant, so rows from different origin zones order by true chronology and changing the display zone never reorders them. The display zone resolves from the column, then the grid, then the viewer, and is nameable in the cell (Europe/London (BST)), with showOrigin: true when the origin differs. Grouping buckets by civil day in the display zone, so a 23- or 25-hour daylight-saving day still collapses to one bucket. Excel export writes the display-zone wall clock and names the zone, never a silent shift to UTC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types the grid reads from your data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A column that declares no type takes one from the rows. The first hundred non-empty values are sampled, and a type is adopted only if every one matches; anything mixed stays text and says so once on the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sku&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;        &lt;span class="c1"&gt;// text&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;// number: aligned right, numeric filter&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shipped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="c1"&gt;// date&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expedited&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// boolean: checkbox editor&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the core names are ever inferred, so a column of IP addresses or durations is text until you name the type. Inference never overrules a decision you made, and type: false turns sampling off. Sorting is not what you gain by declaring: the default comparator is already value-aware, so an undeclared IPv4 column already orders correctly across 128.0.0.1. What a declared type settles is everything around the sort: the editor, the parser, the formatter, the filter kind, the storage, and the export.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building a type to order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two factories cover most custom cases. createUnitType you have seen. createRadixType builds a register view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dataTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;reg32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LatticeGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRadixType&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;radix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bitWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reg32&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="c1"&gt;// 170 → 0b00000000 00000000 00000000 10101010&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Base 10 is deliberately not among the radices: a decimal number is what number is for, with grouping, decimals and currency that a radix formatter has no concept of. Pass radix: 10 and the console names the supported set and falls back to hex, rather than producing something that looks like a number column and is not one.&lt;/p&gt;

&lt;p&gt;When a factory is not enough, a type is a plain object with a small set of properties for you to set:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Member&lt;/th&gt;
&lt;th&gt;What it decides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;base&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The storage family: text, number, boolean, date, dateString or object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;extends&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The type to inherit from; &lt;code&gt;defaults&lt;/code&gt; merge rather than replace, so a derived type overrides one default and keeps the rest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;matches&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether a sampled value belongs to this type, for inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;format&lt;/code&gt; / &lt;code&gt;parse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Display text out, typed or pasted text back in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compare&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;defaults&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The filter kind, editor, footer total, alignment and cell renderer a column gets for free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;float64&lt;/code&gt;, &lt;code&gt;int32&lt;/code&gt;, &lt;code&gt;bitset&lt;/code&gt;, &lt;code&gt;dictionary&lt;/code&gt; or &lt;code&gt;object&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;totals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which aggregates mean something for this type, and the type's own reduction for each&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last member is the one we are really finding useful. A type can declare which aggregates are meaningful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Formats, when you need them&lt;/strong&gt;&lt;br&gt;
On top of a type sits an optional format, with a shorthand for the common cases and a full spec when the shorthand runs out:&lt;/p&gt;

&lt;p&gt;A key in format that the resolved type does not read, a typo or a key from a different shape, is never silently dropped. It warns once, by column and key name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I spent a lot of time talking to the people who were using our SAAS for 8 hours a day, looking at grids on the screen. It was their frustrations and comments that led to Lattice Grid. Making their lives easier so they don't have to type in 10GB when something they are pasting into a cell says 10000MB. Making sure they don't enter 10,000GB by mistake.&lt;/p&gt;

&lt;p&gt;The grid carries the rules, so your users do not have to carry them in their heads, and a wrong number fails loudly at construction instead of quietly in a report later.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>datagrid</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
