How to check if a column type is defined?

The instruction

\newcolumntype{N}{...}

defines the command \NC@find@N. You can check it with

\ifcsname NC@find@\string#1\endcsname

A \safenewcolumntype command can be defined by

\makeatletter
\newcommand\@gobbleoptandone[2][]{}
\expandafter\let\csname safenct@l\endcsname\@empty
\expandafter\let\csname safenct@c\endcsname\@empty
\expandafter\let\csname safenct@r\endcsname\@empty
\expandafter\let\csname safenct@p\endcsname\@empty
\expandafter\let\csname safenct@m\endcsname\@empty
\expandafter\let\csname safenct@b\endcsname\@empty
\expandafter\let\csname safenct@@\endcsname\@empty
\expandafter\let\csname safenct@!\endcsname\@empty
\expandafter\let\csname safenct@|\endcsname\@empty
\expandafter\let\csname safenct@<\endcsname\@empty
\expandafter\let\csname safenct@>\endcsname\@empty
\expandafter\let\csname safenct@=\endcsname\@empty
\def\safenct@error#1{\PackageError{mypackage}
    {Column type `\string#1' already defined}
    {The column type you're trying to define is already\MessageBreak
     existent; I'll ignore this new definition}}
\newcommand{\safenewcolumntype}[1]{%
  \@tempswafalse
  \ifcsname NC@find@\string#1\endcsname
    \safenct@error{#1}\@tempswatrue
  \fi
  \ifcsname safenct@#1\endcsname
    \safenct@error{#1}\@tempswatrue
  \fi
  \if@tempswa
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi{\@gobbleoptandone}{\newcolumntype{#1}}%
}
\makeatother

If the column type is already defined we put before the rest of the token list the command \@gobbleoptandone that will gobble the optional argument and the mandatory one: \safenewcolumntype{l}[1]{...} would become

\@gobbleoptandone[1]{...}

and all is good.

Otherwise we put \newcolumntype{#1} that will perform its duty. Of course it would be better to patch the original \newcolumntype command.

Patching the original definition

With the code between \makeatletter and \makeatother, we redefine the \newcolumntype command in such a way that it checks whether the column type is already defined or not and, in the former case, it disallows redefining the column type. One could define also a \renewcolumntype command, but it doesn't seem this important.

\documentclass{article}
\usepackage{array}

\makeatletter
\newcommand\@gobbleoptandone[2][]{}
\def\newcolumntype#1{%
  \@tempswafalse
  \edef\NC@char{\string#1}%
  \@ifundefined{NC@find@\NC@char}%
    {\@tfor\next:=<>clrmbp@!|\do
      {\if\next\NC@char
         \PackageError{safenct}{`\NC@char' is a primitive column type}
           {You're trying to redefine a primitive column type;\MessageBreak
            the redefinition will be ignored}%
         \global\@safenct@deftrue
       \fi}%
    \if@tempswa\else\NC@list\expandafter{\the\NC@list\NC@do#1}\fi}%
    {\PackageError{safenct}{Column `\NC@char' is already defined}
       {This column type is already defined;\MessageBreak
        the redefinition will be ignored}%
     \@tempswatrue}%
  \if@tempswa
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\@gobbleoptandone}
  {\@namedef{NC@find@\NC@char}##1#1{\NC@{##1}}%
   \@ifnextchar[{\newcol@{\NC@char}}{\newcol@{\NC@char}[0]}}
}

\makeatother

\newcolumntype{N}{>{Y}l<{Z}}
\newcolumntype{N}{r}
\newcolumntype{l}{r}

\begin{document}
\begin{tabular}{N}
xxxx\\
x\\
xx
\end{tabular}
\end{document}

When \newcolumntype{x} is scanned, the routine sets the scratch conditional to false and checks if x is among the primitive column types and, if it is, issues an error message and sets the temporary conditional to true. Then it checks whether x is among the "derived" column types, that is, defined via \newcolumntype itself. If it is, the scratch conditional is again set to false. Finally, as in the code presented before, the proper action is taken: if the conditional is false the following part of the code for \newcolumntype is gobbled, otherwise the definition is performed.


Although this does not directly answer your question, the array package provides \showcols which (according to the array documentation) does the following:

A list of all the currently active \newcolumntype definitions is sent to the terminal and .log file.

For example, if your .tex file contains:

\newcolumntype{X}{l}
\newcolumntype{Y}{@{}c<{\hspace{1ex}}@{\ }}
\showcols

your .log file will contain

Column X -> l
Column Y -> @{}c<{\hspace {1ex}}@{\ }